kref_put(&block->b_count, nlmsvc_free_block);
 }
 
-static void nlmsvc_act_mark(struct nlm_host *host, struct nlm_file *file)
-{
-       struct nlm_block *block;
-
-       down(&file->f_sema);
-       list_for_each_entry(block, &file->f_blocks, b_flist)
-               block->b_host->h_inuse = 1;
-       up(&file->f_sema);
-}
-
-static void nlmsvc_act_unlock(struct nlm_host *host, struct nlm_file *file)
+/*
+ * Loop over all blocks and delete blocks held by
+ * a matching host.
+ */
+void nlmsvc_traverse_blocks(struct nlm_host *host,
+                       struct nlm_file *file,
+                       nlm_host_match_fn_t match)
 {
        struct nlm_block *block, *next;
 
 restart:
        down(&file->f_sema);
        list_for_each_entry_safe(block, next, &file->f_blocks, b_flist) {
-               if (host != NULL && host != block->b_host)
+               if (!match(block->b_host, host))
                        continue;
                /* Do not destroy blocks that are not on
                 * the global retry list - why? */
        up(&file->f_sema);
 }
 
-/*
- * Loop over all blocks and perform the action specified.
- * (NLM_ACT_CHECK handled by nlmsvc_inspect_file).
- */
-void
-nlmsvc_traverse_blocks(struct nlm_host *host, struct nlm_file *file, int action)
-{
-       if (action == NLM_ACT_MARK)
-               nlmsvc_act_mark(host, file);
-       else
-               nlmsvc_act_unlock(host, file);
-}
-
 /*
  * Initialize arguments for GRANTED call. The nlm_rqst structure
  * has been cleared already.
 
  * action.
  */
 static int
-nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, int action)
+nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
+                       nlm_host_match_fn_t match)
 {
        struct inode     *inode = nlmsvc_file_inode(file);
        struct file_lock *fl;
 
                /* update current lock count */
                file->f_locks++;
+
                lockhost = (struct nlm_host *) fl->fl_owner;
-               if (action == NLM_ACT_MARK)
-                       lockhost->h_inuse = 1;
-               else if (action == NLM_ACT_CHECK)
-                       return 1;
-               else if (action == NLM_ACT_UNLOCK) {
+               if (match(lockhost, host)) {
                        struct file_lock lock = *fl;
 
-                       if (host && lockhost != host)
-                               continue;
-
                        lock.fl_type  = F_UNLCK;
                        lock.fl_start = 0;
                        lock.fl_end   = OFFSET_MAX;
 }
 
 /*
- * Operate on a single file
+ * Inspect a single file
+ */
+static inline int
+nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, nlm_host_match_fn_t match)
+{
+       nlmsvc_traverse_blocks(host, file, match);
+       nlmsvc_traverse_shares(host, file, match);
+       return nlm_traverse_locks(host, file, match);
+}
+
+/*
+ * Quick check whether there are still any locks, blocks or
+ * shares on a given file.
  */
 static inline int
-nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, int action)
+nlm_file_inuse(struct nlm_file *file)
 {
-       if (action == NLM_ACT_CHECK) {
-               /* Fast path for mark and sweep garbage collection */
-               if (file->f_count || list_empty(&file->f_blocks) || file->f_shares)
+       struct inode     *inode = nlmsvc_file_inode(file);
+       struct file_lock *fl;
+
+       if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares)
+               return 1;
+
+       for (fl = inode->i_flock; fl; fl = fl->fl_next) {
+               if (fl->fl_lmops == &nlmsvc_lock_operations)
                        return 1;
-       } else {
-               nlmsvc_traverse_blocks(host, file, action);
-               nlmsvc_traverse_shares(host, file, action);
        }
-       return nlm_traverse_locks(host, file, action);
+       file->f_locks = 0;
+       return 0;
 }
 
 /*
  * Loop over all files in the file table.
  */
 static int
-nlm_traverse_files(struct nlm_host *host, int action)
+nlm_traverse_files(struct nlm_host *host, nlm_host_match_fn_t match)
 {
        struct hlist_node *pos, *next;
        struct nlm_file *file;
 
                        /* Traverse locks, blocks and shares of this file
                         * and update file->f_locks count */
-                       if (nlm_inspect_file(host, file, action))
+                       if (nlm_inspect_file(host, file, match))
                                ret = 1;
 
                        mutex_lock(&nlm_file_mutex);
        mutex_lock(&nlm_file_mutex);
 
        /* If there are no more locks etc, delete the file */
-       if(--file->f_count == 0) {
-               if(!nlm_inspect_file(NULL, file, NLM_ACT_CHECK))
-                       nlm_delete_file(file);
-       }
+       if (--file->f_count == 0 && !nlm_file_inuse(file))
+               nlm_delete_file(file);
 
        mutex_unlock(&nlm_file_mutex);
 }
 
+/*
+ * Helpers function for resource traversal
+ *
+ * nlmsvc_mark_host:
+ *     used by the garbage collector; simply sets h_inuse.
+ *     Always returns 0.
+ *
+ * nlmsvc_same_host:
+ *     returns 1 iff the two hosts match. Used to release
+ *     all resources bound to a specific host.
+ *
+ * nlmsvc_is_client:
+ *     returns 1 iff the host is a client.
+ *     Used by nlmsvc_invalidate_all
+ */
+static int
+nlmsvc_mark_host(struct nlm_host *host, struct nlm_host *dummy)
+{
+       host->h_inuse = 1;
+       return 0;
+}
+
+static int
+nlmsvc_same_host(struct nlm_host *host, struct nlm_host *other)
+{
+       return host == other;
+}
+
+static int
+nlmsvc_is_client(struct nlm_host *host, struct nlm_host *dummy)
+{
+       return host->h_server;
+}
+
 /*
  * Mark all hosts that still hold resources
  */
 nlmsvc_mark_resources(void)
 {
        dprintk("lockd: nlmsvc_mark_resources\n");
-
-       nlm_traverse_files(NULL, NLM_ACT_MARK);
+       nlm_traverse_files(NULL, nlmsvc_mark_host);
 }
 
 /*
 {
        dprintk("lockd: nlmsvc_free_host_resources\n");
 
-       if (nlm_traverse_files(host, NLM_ACT_UNLOCK)) {
+       if (nlm_traverse_files(host, nlmsvc_same_host)) {
                printk(KERN_WARNING
                        "lockd: couldn't remove all locks held by %s\n",
                        host->h_name);
 nlmsvc_invalidate_all(void)
 {
        struct nlm_host *host;
+
+       /* Release all locks held by NFS clients.
+        * Previously, the code would call
+        * nlmsvc_free_host_resources for each client in
+        * turn, which is about as inefficient as it gets.
+        * Now we just do it once in nlm_traverse_files.
+        */
+       nlm_traverse_files(NULL, nlmsvc_is_client);
+
        while ((host = nlm_find_client()) != NULL) {
-               nlmsvc_free_host_resources(host);
                host->h_expires = 0;
                host->h_killed = 1;
                nlm_release_host(host);