nfsd4: remove some dprintk's
[safe/jmp/linux-2.6] / fs / lockd / svclock.c
index 5c054b2..83ee342 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/sunrpc/svc.h>
 #include <linux/lockd/nlm.h>
 #include <linux/lockd/lockd.h>
+#include <linux/kthread.h>
 
 #define NLMDBG_FACILITY                NLMDBG_SVCLOCK
 
@@ -128,9 +129,9 @@ nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock)
 
 static inline int nlm_cookie_match(struct nlm_cookie *a, struct nlm_cookie *b)
 {
-       if(a->len != b->len)
+       if (a->len != b->len)
                return 0;
-       if(memcmp(a->data,b->data,a->len))
+       if (memcmp(a->data, b->data, a->len))
                return 0;
        return 1;
 }
@@ -171,19 +172,15 @@ found:
  * GRANTED_RES message by cookie, without having to rely on the client's IP
  * address. --okir
  */
-static inline struct nlm_block *
-nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file,
-                               struct nlm_lock *lock, struct nlm_cookie *cookie)
+static struct nlm_block *
+nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_host *host,
+                   struct nlm_file *file, struct nlm_lock *lock,
+                   struct nlm_cookie *cookie)
 {
        struct nlm_block        *block;
-       struct nlm_host         *host;
        struct nlm_rqst         *call = NULL;
 
-       /* Create host handle for callback */
-       host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len);
-       if (host == NULL)
-               return NULL;
-
+       nlm_get_host(host);
        call = nlm_alloc_call(host);
        if (call == NULL)
                return NULL;
@@ -210,6 +207,7 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file,
        block->b_daemon = rqstp->rq_server;
        block->b_host   = host;
        block->b_file   = file;
+       block->b_fl = NULL;
        file->f_count++;
 
        /* Add to file's list of blocks */
@@ -230,8 +228,7 @@ failed:
 }
 
 /*
- * Delete a block. If the lock was cancelled or the grant callback
- * failed, unlock is set to 1.
+ * Delete a block.
  * It is the caller's responsibility to check whether the file
  * can be closed hereafter.
  */
@@ -261,6 +258,7 @@ static void nlmsvc_free_block(struct kref *kref)
        nlmsvc_freegrantargs(block->b_call);
        nlm_release_call(block->b_call);
        nlm_release_file(block->b_file);
+       kfree(block->b_fl);
        kfree(block);
 }
 
@@ -331,14 +329,40 @@ static void nlmsvc_freegrantargs(struct nlm_rqst *call)
 }
 
 /*
+ * Deferred lock request handling for non-blocking lock
+ */
+static __be32
+nlmsvc_defer_lock_rqst(struct svc_rqst *rqstp, struct nlm_block *block)
+{
+       __be32 status = nlm_lck_denied_nolocks;
+
+       block->b_flags |= B_QUEUED;
+
+       nlmsvc_insert_block(block, NLM_TIMEOUT);
+
+       block->b_cache_req = &rqstp->rq_chandle;
+       if (rqstp->rq_chandle.defer) {
+               block->b_deferred_req =
+                       rqstp->rq_chandle.defer(block->b_cache_req);
+               if (block->b_deferred_req != NULL)
+                       status = nlm_drop_reply;
+       }
+       dprintk("lockd: nlmsvc_defer_lock_rqst block %p flags %d status %d\n",
+               block, block->b_flags, ntohl(status));
+
+       return status;
+}
+
+/*
  * Attempt to establish a lock, and if it can't be granted, block it
  * if required.
  */
 __be32
 nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
-                       struct nlm_lock *lock, int wait, struct nlm_cookie *cookie)
+           struct nlm_host *host, struct nlm_lock *lock, int wait,
+           struct nlm_cookie *cookie, int reclaim)
 {
-       struct nlm_block        *block, *newblock = NULL;
+       struct nlm_block        *block = NULL;
        int                     error;
        __be32                  ret;
 
@@ -350,30 +374,74 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
                                (long long)lock->fl.fl_end,
                                wait);
 
-
-       lock->fl.fl_flags &= ~FL_SLEEP;
-again:
        /* Lock file against concurrent access */
        mutex_lock(&file->f_mutex);
-       /* Get existing block (in case client is busy-waiting) */
+       /* Get existing block (in case client is busy-waiting)
+        * or create new block
+        */
        block = nlmsvc_lookup_block(file, lock);
        if (block == NULL) {
-               if (newblock != NULL)
-                       lock = &newblock->b_call->a_args.lock;
-       } else
+               block = nlmsvc_create_block(rqstp, host, file, lock, cookie);
+               ret = nlm_lck_denied_nolocks;
+               if (block == NULL)
+                       goto out;
                lock = &block->b_call->a_args.lock;
+       } else
+               lock->fl.fl_flags &= ~FL_SLEEP;
 
-       error = posix_lock_file(file->f_file, &lock->fl);
-       lock->fl.fl_flags &= ~FL_SLEEP;
+       if (block->b_flags & B_QUEUED) {
+               dprintk("lockd: nlmsvc_lock deferred block %p flags %d\n",
+                                                       block, block->b_flags);
+               if (block->b_granted) {
+                       nlmsvc_unlink_block(block);
+                       ret = nlm_granted;
+                       goto out;
+               }
+               if (block->b_flags & B_TIMED_OUT) {
+                       nlmsvc_unlink_block(block);
+                       ret = nlm_lck_denied;
+                       goto out;
+               }
+               ret = nlm_drop_reply;
+               goto out;
+       }
 
-       dprintk("lockd: posix_lock_file returned %d\n", error);
+       if (locks_in_grace() && !reclaim) {
+               ret = nlm_lck_denied_grace_period;
+               goto out;
+       }
+       if (reclaim && !locks_in_grace()) {
+               ret = nlm_lck_denied_grace_period;
+               goto out;
+       }
 
-       switch(error) {
+       if (!wait)
+               lock->fl.fl_flags &= ~FL_SLEEP;
+       error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
+       lock->fl.fl_flags &= ~FL_SLEEP;
+
+       dprintk("lockd: vfs_lock_file returned %d\n", error);
+       switch (error) {
                case 0:
                        ret = nlm_granted;
                        goto out;
                case -EAGAIN:
-                       break;
+                       /*
+                        * If this is a blocking request for an
+                        * already pending lock request then we need
+                        * to put it back on lockd's block list
+                        */
+                       if (wait)
+                               break;
+                       ret = nlm_lck_denied;
+                       goto out;
+               case FILE_LOCK_DEFERRED:
+                       if (wait)
+                               break;
+                       /* Filesystem lock operation is in progress
+                          Add it to the queue waiting for callback */
+                       ret = nlmsvc_defer_lock_rqst(rqstp, block);
+                       goto out;
                case -EDEADLK:
                        ret = nlm_deadlock;
                        goto out;
@@ -382,31 +450,12 @@ again:
                        goto out;
        }
 
-       ret = nlm_lck_denied;
-       if (!wait)
-               goto out;
-
        ret = nlm_lck_blocked;
-       if (block != NULL)
-               goto out;
-
-       /* If we don't have a block, create and initialize it. Then
-        * retry because we may have slept in kmalloc. */
-       /* We have to release f_mutex as nlmsvc_create_block may try to
-        * to claim it while doing host garbage collection */
-       if (newblock == NULL) {
-               mutex_unlock(&file->f_mutex);
-               dprintk("lockd: blocking on this lock (allocating).\n");
-               if (!(newblock = nlmsvc_create_block(rqstp, file, lock, cookie)))
-                       return nlm_lck_denied_nolocks;
-               goto again;
-       }
 
        /* Append to list of blocked */
-       nlmsvc_insert_block(newblock, NLM_NEVER);
+       nlmsvc_insert_block(block, NLM_NEVER);
 out:
        mutex_unlock(&file->f_mutex);
-       nlmsvc_release_block(newblock);
        nlmsvc_release_block(block);
        dprintk("lockd: nlmsvc_lock returned %u\n", ret);
        return ret;
@@ -416,9 +465,14 @@ out:
  * Test for presence of a conflicting lock.
  */
 __be32
-nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock,
-                                      struct nlm_lock *conflock)
+nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
+               struct nlm_host *host, struct nlm_lock *lock,
+               struct nlm_lock *conflock, struct nlm_cookie *cookie)
 {
+       struct nlm_block        *block = NULL;
+       int                     error;
+       __be32                  ret;
+
        dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n",
                                file->f_file->f_path.dentry->d_inode->i_sb->s_id,
                                file->f_file->f_path.dentry->d_inode->i_ino,
@@ -426,19 +480,78 @@ nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock,
                                (long long)lock->fl.fl_start,
                                (long long)lock->fl.fl_end);
 
-       if (posix_test_lock(file->f_file, &lock->fl, &conflock->fl)) {
-               dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n",
-                               conflock->fl.fl_type,
-                               (long long)conflock->fl.fl_start,
-                               (long long)conflock->fl.fl_end);
-               conflock->caller = "somehost";  /* FIXME */
-               conflock->len = strlen(conflock->caller);
-               conflock->oh.len = 0;           /* don't return OH info */
-               conflock->svid = conflock->fl.fl_pid;
-               return nlm_lck_denied;
+       /* Get existing block (in case client is busy-waiting) */
+       block = nlmsvc_lookup_block(file, lock);
+
+       if (block == NULL) {
+               struct file_lock *conf = kzalloc(sizeof(*conf), GFP_KERNEL);
+
+               if (conf == NULL)
+                       return nlm_granted;
+               block = nlmsvc_create_block(rqstp, host, file, lock, cookie);
+               if (block == NULL) {
+                       kfree(conf);
+                       return nlm_granted;
+               }
+               block->b_fl = conf;
+       }
+       if (block->b_flags & B_QUEUED) {
+               dprintk("lockd: nlmsvc_testlock deferred block %p flags %d fl %p\n",
+                       block, block->b_flags, block->b_fl);
+               if (block->b_flags & B_TIMED_OUT) {
+                       nlmsvc_unlink_block(block);
+                       ret = nlm_lck_denied;
+                       goto out;
+               }
+               if (block->b_flags & B_GOT_CALLBACK) {
+                       nlmsvc_unlink_block(block);
+                       if (block->b_fl != NULL
+                                       && block->b_fl->fl_type != F_UNLCK) {
+                               lock->fl = *block->b_fl;
+                               goto conf_lock;
+                       } else {
+                               ret = nlm_granted;
+                               goto out;
+                       }
+               }
+               ret = nlm_drop_reply;
+               goto out;
+       }
+
+       if (locks_in_grace()) {
+               ret = nlm_lck_denied_grace_period;
+               goto out;
+       }
+       error = vfs_test_lock(file->f_file, &lock->fl);
+       if (error == FILE_LOCK_DEFERRED) {
+               ret = nlmsvc_defer_lock_rqst(rqstp, block);
+               goto out;
+       }
+       if (error) {
+               ret = nlm_lck_denied_nolocks;
+               goto out;
+       }
+       if (lock->fl.fl_type == F_UNLCK) {
+               ret = nlm_granted;
+               goto out;
        }
 
-       return nlm_granted;
+conf_lock:
+       dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n",
+               lock->fl.fl_type, (long long)lock->fl.fl_start,
+               (long long)lock->fl.fl_end);
+       conflock->caller = "somehost";  /* FIXME */
+       conflock->len = strlen(conflock->caller);
+       conflock->oh.len = 0;           /* don't return OH info */
+       conflock->svid = lock->fl.fl_pid;
+       conflock->fl.fl_type = lock->fl.fl_type;
+       conflock->fl.fl_start = lock->fl.fl_start;
+       conflock->fl.fl_end = lock->fl.fl_end;
+       ret = nlm_lck_denied;
+out:
+       if (block)
+               nlmsvc_release_block(block);
+       return ret;
 }
 
 /*
@@ -464,7 +577,7 @@ nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock)
        nlmsvc_cancel_blocked(file, lock);
 
        lock->fl.fl_type = F_UNLCK;
-       error = posix_lock_file(file->f_file, &lock->fl);
+       error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
 
        return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
 }
@@ -489,10 +602,15 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock)
                                (long long)lock->fl.fl_start,
                                (long long)lock->fl.fl_end);
 
+       if (locks_in_grace())
+               return nlm_lck_denied_grace_period;
+
        mutex_lock(&file->f_mutex);
        block = nlmsvc_lookup_block(file, lock);
        mutex_unlock(&file->f_mutex);
        if (block != NULL) {
+               vfs_cancel_lock(block->b_file->f_file,
+                               &block->b_call->a_args.lock.fl);
                status = nlmsvc_unlink_block(block);
                nlmsvc_release_block(block);
        }
@@ -500,6 +618,63 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock)
 }
 
 /*
+ * This is a callback from the filesystem for VFS file lock requests.
+ * It will be used if fl_grant is defined and the filesystem can not
+ * respond to the request immediately.
+ * For GETLK request it will copy the reply to the nlm_block.
+ * For SETLK or SETLKW request it will get the local posix lock.
+ * In all cases it will move the block to the head of nlm_blocked q where
+ * nlmsvc_retry_blocked() can send back a reply for SETLKW or revisit the
+ * deferred rpc for GETLK and SETLK.
+ */
+static void
+nlmsvc_update_deferred_block(struct nlm_block *block, struct file_lock *conf,
+                            int result)
+{
+       block->b_flags |= B_GOT_CALLBACK;
+       if (result == 0)
+               block->b_granted = 1;
+       else
+               block->b_flags |= B_TIMED_OUT;
+       if (conf) {
+               if (block->b_fl)
+                       __locks_copy_lock(block->b_fl, conf);
+       }
+}
+
+static int nlmsvc_grant_deferred(struct file_lock *fl, struct file_lock *conf,
+                                       int result)
+{
+       struct nlm_block *block;
+       int rc = -ENOENT;
+
+       lock_kernel();
+       list_for_each_entry(block, &nlm_blocked, b_list) {
+               if (nlm_compare_locks(&block->b_call->a_args.lock.fl, fl)) {
+                       dprintk("lockd: nlmsvc_notify_blocked block %p flags %d\n",
+                                                       block, block->b_flags);
+                       if (block->b_flags & B_QUEUED) {
+                               if (block->b_flags & B_TIMED_OUT) {
+                                       rc = -ENOLCK;
+                                       break;
+                               }
+                               nlmsvc_update_deferred_block(block, conf, result);
+                       } else if (result == 0)
+                               block->b_granted = 1;
+
+                       nlmsvc_insert_block(block, 0);
+                       svc_wake_up(block->b_daemon);
+                       rc = 0;
+                       break;
+               }
+       }
+       unlock_kernel();
+       if (rc == -ENOENT)
+               printk(KERN_WARNING "lockd: grant for unknown block\n");
+       return rc;
+}
+
+/*
  * Unblock a blocked lock request. This is a callback invoked from the
  * VFS layer when a lock on which we blocked is removed.
  *
@@ -531,6 +706,7 @@ static int nlmsvc_same_owner(struct file_lock *fl1, struct file_lock *fl2)
 struct lock_manager_operations nlmsvc_lock_operations = {
        .fl_compare_owner = nlmsvc_same_owner,
        .fl_notify = nlmsvc_notify_blocked,
+       .fl_grant = nlmsvc_grant_deferred,
 };
 
 /*
@@ -553,6 +729,8 @@ nlmsvc_grant_blocked(struct nlm_block *block)
 
        dprintk("lockd: grant blocked lock %p\n", block);
 
+       kref_get(&block->b_count);
+
        /* Unlink block request from list */
        nlmsvc_unlink_block(block);
 
@@ -566,20 +744,22 @@ nlmsvc_grant_blocked(struct nlm_block *block)
 
        /* Try the lock operation again */
        lock->fl.fl_flags |= FL_SLEEP;
-       error = posix_lock_file(file->f_file, &lock->fl);
+       error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
        lock->fl.fl_flags &= ~FL_SLEEP;
 
        switch (error) {
        case 0:
                break;
-       case -EAGAIN:
-               dprintk("lockd: lock still blocked\n");
+       case FILE_LOCK_DEFERRED:
+               dprintk("lockd: lock still blocked error %d\n", error);
                nlmsvc_insert_block(block, NLM_NEVER);
+               nlmsvc_release_block(block);
                return;
        default:
                printk(KERN_WARNING "lockd: unexpected error %d in %s!\n",
-                               -error, __FUNCTION__);
+                               -error, __func__);
                nlmsvc_insert_block(block, 10 * HZ);
+               nlmsvc_release_block(block);
                return;
        }
 
@@ -588,14 +768,20 @@ callback:
        dprintk("lockd: GRANTing blocked lock.\n");
        block->b_granted = 1;
 
-       /* Schedule next grant callback in 30 seconds */
-       nlmsvc_insert_block(block, 30 * HZ);
+       /* keep block on the list, but don't reattempt until the RPC
+        * completes or the submission fails
+        */
+       nlmsvc_insert_block(block, NLM_NEVER);
 
-       /* Call the client */
-       kref_get(&block->b_count);
-       if (nlm_async_call(block->b_call, NLMPROC_GRANTED_MSG,
-                                               &nlmsvc_grant_ops) < 0)
-               nlmsvc_release_block(block);
+       /* Call the client -- use a soft RPC task since nlmsvc_retry_blocked
+        * will queue up a new one if this one times out
+        */
+       error = nlm_async_call(block->b_call, NLMPROC_GRANTED_MSG,
+                               &nlmsvc_grant_ops);
+
+       /* RPC submission failed, wait a bit and retry */
+       if (error < 0)
+               nlmsvc_insert_block(block, 10 * HZ);
 }
 
 /*
@@ -614,6 +800,18 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
 
        dprintk("lockd: GRANT_MSG RPC callback\n");
 
+       lock_kernel();
+       /* if the block is not on a list at this point then it has
+        * been invalidated. Don't try to requeue it.
+        *
+        * FIXME: it's possible that the block is removed from the list
+        * after this check but before the nlmsvc_insert_block. In that
+        * case it will be added back. Perhaps we need better locking
+        * for nlm_blocked?
+        */
+       if (list_empty(&block->b_list))
+               goto out;
+
        /* Technically, we should down the file semaphore here. Since we
         * move the block towards the head of the queue only, no harm
         * can be done, though. */
@@ -626,13 +824,17 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
        }
        nlmsvc_insert_block(block, timeout);
        svc_wake_up(block->b_daemon);
+out:
+       unlock_kernel();
 }
 
 static void nlmsvc_grant_release(void *data)
 {
        struct nlm_rqst         *call = data;
 
+       lock_kernel();
        nlmsvc_release_block(call->a_block);
+       unlock_kernel();
 }
 
 static const struct rpc_call_ops nlmsvc_grant_ops = {
@@ -645,7 +847,7 @@ static const struct rpc_call_ops nlmsvc_grant_ops = {
  * block.
  */
 void
-nlmsvc_grant_reply(struct nlm_cookie *cookie, u32 status)
+nlmsvc_grant_reply(struct nlm_cookie *cookie, __be32 status)
 {
        struct nlm_block        *block;
 
@@ -655,7 +857,7 @@ nlmsvc_grant_reply(struct nlm_cookie *cookie, u32 status)
                return;
 
        if (block) {
-               if (status == NLM_LCK_DENIED_GRACE_PERIOD) {
+               if (status == nlm_lck_denied_grace_period) {
                        /* Try again in a couple of seconds */
                        nlmsvc_insert_block(block, 10 * HZ);
                } else {
@@ -667,6 +869,23 @@ nlmsvc_grant_reply(struct nlm_cookie *cookie, u32 status)
        nlmsvc_release_block(block);
 }
 
+/* Helper function to handle retry of a deferred block.
+ * If it is a blocking lock, call grant_blocked.
+ * For a non-blocking lock or test lock, revisit the request.
+ */
+static void
+retry_deferred_block(struct nlm_block *block)
+{
+       if (!(block->b_flags & B_GOT_CALLBACK))
+               block->b_flags |= B_TIMED_OUT;
+       nlmsvc_insert_block(block, NLM_TIMEOUT);
+       dprintk("revisit block %p flags %d\n",  block, block->b_flags);
+       if (block->b_deferred_req) {
+               block->b_deferred_req->revisit(block->b_deferred_req, 0);
+               block->b_deferred_req = NULL;
+       }
+}
+
 /*
  * Retry all blocked locks that have been notified. This is where lockd
  * picks up locks that can be granted, or grant notifications that must
@@ -678,21 +897,24 @@ nlmsvc_retry_blocked(void)
        unsigned long   timeout = MAX_SCHEDULE_TIMEOUT;
        struct nlm_block *block;
 
-       while (!list_empty(&nlm_blocked)) {
+       while (!list_empty(&nlm_blocked) && !kthread_should_stop()) {
                block = list_entry(nlm_blocked.next, struct nlm_block, b_list);
 
                if (block->b_when == NLM_NEVER)
                        break;
-               if (time_after(block->b_when,jiffies)) {
+               if (time_after(block->b_when, jiffies)) {
                        timeout = block->b_when - jiffies;
                        break;
                }
 
                dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n",
                        block, block->b_when);
-               kref_get(&block->b_count);
-               nlmsvc_grant_blocked(block);
-               nlmsvc_release_block(block);
+               if (block->b_flags & B_QUEUED) {
+                       dprintk("nlmsvc_retry_blocked delete block (%p, granted=%d, flags=%d)\n",
+                               block, block->b_granted, block->b_flags);
+                       retry_deferred_block(block);
+               } else
+                       nlmsvc_grant_blocked(block);
        }
 
        return timeout;