const char *callout_string,
                                             void *aux);
 
+or:
+
+       struct key *request_key_async(const struct key_type *type,
+                                     const char *description,
+                                     const char *callout_string);
+
+or:
+
+       struct key *request_key_async_with_auxdata(const struct key_type *type,
+                                                  const char *description,
+                                                  const char *callout_string,
+                                                  void *aux);
+
 Or by userspace invoking the request_key system call:
 
        key_serial_t request_key(const char *type,
 destroyed.  The kernel interface returns a pointer directly to the key, and
 it's up to the caller to destroy the key.
 
-The request_key_with_auxdata() call is like the in-kernel request_key() call,
-except that it permits auxiliary data to be passed to the upcaller (the default
-is NULL).  This is only useful for those key types that define their own upcall
-mechanism rather than using /sbin/request-key.
+The request_key*_with_auxdata() calls are like the in-kernel request_key*()
+calls, except that they permit auxiliary data to be passed to the upcaller (the
+default is NULL).  This is only useful for those key types that define their
+own upcall mechanism rather than using /sbin/request-key.
+
+The two async in-kernel calls may return keys that are still in the process of
+being constructed.  The two non-async ones will wait for construction to
+complete first.
 
 The userspace interface links the key to a keyring associated with the process
 to prevent the key from going away, and returns the serial number of the key to
 
 
 This service allows cryptographic keys, authentication tokens, cross-domain
 user mappings, and similar to be cached in the kernel for the use of
-filesystems other kernel services.
+filesystems and other kernel services.
 
 Keyrings are permitted; these are a special type of key that can hold links to
 other keys. Processes each have three standard keyring subscriptions that a
 two different users opening the same file is left to the filesystem author to
 solve.
 
+To access the key manager, the following header must be #included:
+
+       <linux/key.h>
+
+Specific key types should have a header file under include/keys/ that should be
+used to access that type.  For keys of type "user", for example, that would be:
+
+       <keys/user-type.h>
+
 Note that there are two different types of pointers to keys that may be
 encountered:
 
     passed to the key_type->request_key() op if it exists.
 
 
+(*) A key can be requested asynchronously by calling one of:
+
+       struct key *request_key_async(const struct key_type *type,
+                                     const char *description,
+                                     const char *callout_string);
+
+    or:
+
+       struct key *request_key_async_with_auxdata(const struct key_type *type,
+                                                  const char *description,
+                                                  const char *callout_string,
+                                                  void *aux);
+
+    which are asynchronous equivalents of request_key() and
+    request_key_with_auxdata() respectively.
+
+    These two functions return with the key potentially still under
+    construction.  To wait for contruction completion, the following should be
+    called:
+
+       int wait_for_key_construction(struct key *key, bool intr);
+
+    The function will wait for the key to finish being constructed and then
+    invokes key_validate() to return an appropriate value to indicate the state
+    of the key (0 indicates the key is usable).
+
+    If intr is true, then the wait can be interrupted by a signal, in which
+    case error ERESTARTSYS will be returned.
+
+
 (*) When it is no longer required, the key should be released using:
 
        void key_put(struct key *key);
 
 A kernel service may want to define its own key type. For instance, an AFS
 filesystem might want to define a Kerberos 5 ticket key type. To do this, it
-author fills in a struct key_type and registers it with the system.
+author fills in a key_type struct and registers it with the system.
+
+Source files that implement key types should include the following header file:
+
+       <linux/key-type.h>
 
 The structure has a number of fields, some of which are mandatory:
 
      as might happen when the userspace buffer is accessed.
 
 
- (*) int (*request_key)(struct key *key, struct key *authkey, const char *op,
+ (*) int (*request_key)(struct key_construction *cons, const char *op,
                        void *aux);
 
-     This method is optional.  If provided, request_key() and
-     request_key_with_auxdata() will invoke this function rather than
-     upcalling to /sbin/request-key to operate upon a key of this type.
+     This method is optional.  If provided, request_key() and friends will
+     invoke this function rather than upcalling to /sbin/request-key to operate
+     upon a key of this type.
+
+     The aux parameter is as passed to request_key_async_with_auxdata() and
+     similar or is NULL otherwise.  Also passed are the construction record for
+     the key to be operated upon and the operation type (currently only
+     "create").
+
+     This method is permitted to return before the upcall is complete, but the
+     following function must be called under all circumstances to complete the
+     instantiation process, whether or not it succeeds, whether or not there's
+     an error:
+
+       void complete_request_key(struct key_construction *cons, int error);
+
+     The error parameter should be 0 on success, -ve on error.  The
+     construction record is destroyed by this action and the authorisation key
+     will be revoked.  If an error is indicated, the key under construction
+     will be negatively instantiated if it wasn't already instantiated.
+
+     If this method returns an error, that error will be returned to the
+     caller of request_key*().  complete_request_key() must be called prior to
+     returning.
+
+     The key under construction and the authorisation key can be found in the
+     key_construction struct pointed to by cons:
+
+     (*) struct key *key;
+
+        The key under construction.
 
-     The aux parameter is as passed to request_key_with_auxdata() or is NULL
-     otherwise.  Also passed are the key to be operated upon, the
-     authorisation key for this operation and the operation type (currently
-     only "create").
+     (*) struct key *authkey;
 
-     This function should return only when the upcall is complete.  Upon return
-     the authorisation key will be revoked, and the target key will be
-     negatively instantiated if it is still uninstantiated.  The error will be
-     returned to the caller of request_key*().
+        The authorisation key.
 
 
 ============================
 
 
      This is used to extract the error number from a message indicating either
      a local error occurred or a network error occurred.
+
+ (*) Allocate a null key for doing anonymous security.
+
+       struct key *rxrpc_get_null_key(const char *keyname);
+
+     This is used to allocate a null RxRPC key that can be used to indicate
+     anonymous security for a particular domain.
 
 static struct afs_cell *afs_cell_alloc(const char *name, char *vllist)
 {
        struct afs_cell *cell;
+       struct key *key;
        size_t namelen;
        char keyname[4 + AFS_MAXCELLNAME + 1], *cp, *dp, *next;
        int ret;
        do {
                *dp++ = toupper(*cp);
        } while (*cp++);
-       cell->anonymous_key = key_alloc(&key_type_rxrpc, keyname, 0, 0, current,
-                                       KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA);
-       if (IS_ERR(cell->anonymous_key)) {
-               _debug("no key");
-               ret = PTR_ERR(cell->anonymous_key);
-               goto error;
-       }
 
-       ret = key_instantiate_and_link(cell->anonymous_key, NULL, 0,
-                                      NULL, NULL);
-       if (ret < 0) {
-               _debug("instantiate failed");
+       key = rxrpc_get_null_key(keyname);
+       if (IS_ERR(key)) {
+               _debug("no key");
+               ret = PTR_ERR(key);
                goto error;
        }
+       cell->anonymous_key = key;
 
        _debug("anon key %p{%x}",
               cell->anonymous_key, key_serial(cell->anonymous_key));
 
  */
 extern struct key_type key_type_rxrpc;
 
+extern struct key *rxrpc_get_null_key(const char *);
+
 #endif /* _KEYS_USER_TYPE_H */
 
--- /dev/null
+/* Definitions for key type implementations
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_KEY_TYPE_H
+#define _LINUX_KEY_TYPE_H
+
+#include <linux/key.h>
+
+#ifdef CONFIG_KEYS
+
+/*
+ * key under-construction record
+ * - passed to the request_key actor if supplied
+ */
+struct key_construction {
+       struct key      *key;   /* key being constructed */
+       struct key      *authkey;/* authorisation for key being constructed */
+};
+
+typedef int (*request_key_actor_t)(struct key_construction *key,
+                                  const char *op, void *aux);
+
+/*
+ * kernel managed key type definition
+ */
+struct key_type {
+       /* name of the type */
+       const char *name;
+
+       /* default payload length for quota precalculation (optional)
+        * - this can be used instead of calling key_payload_reserve(), that
+        *   function only needs to be called if the real datalen is different
+        */
+       size_t def_datalen;
+
+       /* instantiate a key of this type
+        * - this method should call key_payload_reserve() to determine if the
+        *   user's quota will hold the payload
+        */
+       int (*instantiate)(struct key *key, const void *data, size_t datalen);
+
+       /* update a key of this type (optional)
+        * - this method should call key_payload_reserve() to recalculate the
+        *   quota consumption
+        * - the key must be locked against read when modifying
+        */
+       int (*update)(struct key *key, const void *data, size_t datalen);
+
+       /* match a key against a description */
+       int (*match)(const struct key *key, const void *desc);
+
+       /* clear some of the data from a key on revokation (optional)
+        * - the key's semaphore will be write-locked by the caller
+        */
+       void (*revoke)(struct key *key);
+
+       /* clear the data from a key (optional) */
+       void (*destroy)(struct key *key);
+
+       /* describe a key */
+       void (*describe)(const struct key *key, struct seq_file *p);
+
+       /* read a key's data (optional)
+        * - permission checks will be done by the caller
+        * - the key's semaphore will be readlocked by the caller
+        * - should return the amount of data that could be read, no matter how
+        *   much is copied into the buffer
+        * - shouldn't do the copy if the buffer is NULL
+        */
+       long (*read)(const struct key *key, char __user *buffer, size_t buflen);
+
+       /* handle request_key() for this type instead of invoking
+        * /sbin/request-key (optional)
+        * - key is the key to instantiate
+        * - authkey is the authority to assume when instantiating this key
+        * - op is the operation to be done, usually "create"
+        * - the call must not return until the instantiation process has run
+        *   its course
+        */
+       request_key_actor_t request_key;
+
+       /* internal fields */
+       struct list_head        link;           /* link in types list */
+};
+
+extern struct key_type key_type_keyring;
+
+extern int register_key_type(struct key_type *ktype);
+extern void unregister_key_type(struct key_type *ktype);
+
+extern int key_payload_reserve(struct key *key, size_t datalen);
+extern int key_instantiate_and_link(struct key *key,
+                                   const void *data,
+                                   size_t datalen,
+                                   struct key *keyring,
+                                   struct key *instkey);
+extern int key_negate_and_link(struct key *key,
+                              unsigned timeout,
+                              struct key *keyring,
+                              struct key *instkey);
+extern void complete_request_key(struct key_construction *cons, int error);
+
+#endif /* CONFIG_KEYS */
+#endif /* _LINUX_KEY_TYPE_H */
 
-/* key.h: authentication token and access key management
+/* Authentication token and access key management
  *
- * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2004, 2007 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
        } payload;
 };
 
-/*****************************************************************************/
-/*
- * kernel managed key type definition
- */
-typedef int (*request_key_actor_t)(struct key *key, struct key *authkey,
-                                  const char *op, void *aux);
-
-struct key_type {
-       /* name of the type */
-       const char *name;
-
-       /* default payload length for quota precalculation (optional)
-        * - this can be used instead of calling key_payload_reserve(), that
-        *   function only needs to be called if the real datalen is different
-        */
-       size_t def_datalen;
-
-       /* instantiate a key of this type
-        * - this method should call key_payload_reserve() to determine if the
-        *   user's quota will hold the payload
-        */
-       int (*instantiate)(struct key *key, const void *data, size_t datalen);
-
-       /* update a key of this type (optional)
-        * - this method should call key_payload_reserve() to recalculate the
-        *   quota consumption
-        * - the key must be locked against read when modifying
-        */
-       int (*update)(struct key *key, const void *data, size_t datalen);
-
-       /* match a key against a description */
-       int (*match)(const struct key *key, const void *desc);
-
-       /* clear some of the data from a key on revokation (optional)
-        * - the key's semaphore will be write-locked by the caller
-        */
-       void (*revoke)(struct key *key);
-
-       /* clear the data from a key (optional) */
-       void (*destroy)(struct key *key);
-
-       /* describe a key */
-       void (*describe)(const struct key *key, struct seq_file *p);
-
-       /* read a key's data (optional)
-        * - permission checks will be done by the caller
-        * - the key's semaphore will be readlocked by the caller
-        * - should return the amount of data that could be read, no matter how
-        *   much is copied into the buffer
-        * - shouldn't do the copy if the buffer is NULL
-        */
-       long (*read)(const struct key *key, char __user *buffer, size_t buflen);
-
-       /* handle request_key() for this type instead of invoking
-        * /sbin/request-key (optional)
-        * - key is the key to instantiate
-        * - authkey is the authority to assume when instantiating this key
-        * - op is the operation to be done, usually "create"
-        * - the call must not return until the instantiation process has run
-        *   its course
-        */
-       request_key_actor_t request_key;
-
-       /* internal fields */
-       struct list_head        link;           /* link in types list */
-};
-
-extern struct key_type key_type_keyring;
-
-extern int register_key_type(struct key_type *ktype);
-extern void unregister_key_type(struct key_type *ktype);
-
 extern struct key *key_alloc(struct key_type *type,
                             const char *desc,
                             uid_t uid, gid_t gid,
 #define KEY_ALLOC_QUOTA_OVERRUN        0x0001  /* add to quota, permit even if overrun */
 #define KEY_ALLOC_NOT_IN_QUOTA 0x0002  /* not in quota */
 
-extern int key_payload_reserve(struct key *key, size_t datalen);
-extern int key_instantiate_and_link(struct key *key,
-                                   const void *data,
-                                   size_t datalen,
-                                   struct key *keyring,
-                                   struct key *instkey);
-extern int key_negate_and_link(struct key *key,
-                              unsigned timeout,
-                              struct key *keyring,
-                              struct key *instkey);
 extern void key_revoke(struct key *key);
 extern void key_put(struct key *key);
 
                                            const char *callout_info,
                                            void *aux);
 
+extern struct key *request_key_async(struct key_type *type,
+                                    const char *description,
+                                    const char *callout_info);
+
+extern struct key *request_key_async_with_auxdata(struct key_type *type,
+                                                 const char *description,
+                                                 const char *callout_info,
+                                                 void *aux);
+
+extern int wait_for_key_construction(struct key *key, bool intr);
+
 extern int key_validate(struct key *key);
 
 extern key_ref_t key_create_or_update(key_ref_t keyring,
 
 extern struct key *key_lookup(key_serial_t id);
 
-extern void keyring_replace_payload(struct key *key, void *replacement);
-
 #define key_serial(key) ((key) ? (key)->serial : 0)
 
 /*
 
 #include <linux/skbuff.h>
 #include <linux/poll.h>
 #include <linux/proc_fs.h>
+#include <linux/key-type.h>
 #include <net/net_namespace.h>
 #include <net/sock.h>
 #include <net/af_rxrpc.h>
 
 #include <linux/module.h>
 #include <linux/net.h>
 #include <linux/skbuff.h>
-#include <linux/key.h>
+#include <linux/key-type.h>
 #include <linux/crypto.h>
 #include <net/sock.h>
 #include <net/af_rxrpc.h>
        .destroy        = rxrpc_destroy,
        .describe       = rxrpc_describe,
 };
-
 EXPORT_SYMBOL(key_type_rxrpc);
 
 /*
        _leave(" = -ENOMEM [ins %d]", ret);
        return -ENOMEM;
 }
-
 EXPORT_SYMBOL(rxrpc_get_server_data_key);
+
+/**
+ * rxrpc_get_null_key - Generate a null RxRPC key
+ * @keyname: The name to give the key.
+ *
+ * Generate a null RxRPC key that can be used to indicate anonymous security is
+ * required for a particular domain.
+ */
+struct key *rxrpc_get_null_key(const char *keyname)
+{
+       struct key *key;
+       int ret;
+
+       key = key_alloc(&key_type_rxrpc, keyname, 0, 0, current,
+                       KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA);
+       if (IS_ERR(key))
+               return key;
+
+       ret = key_instantiate_and_link(key, NULL, 0, NULL, NULL);
+       if (ret < 0) {
+               key_revoke(key);
+               key_put(key);
+               return ERR_PTR(ret);
+       }
+
+       return key;
+}
+EXPORT_SYMBOL(rxrpc_get_null_key);
 
 /* internal.h: authentication token and access key management internal defs
  *
- * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2003-5, 2007 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
 #ifndef _INTERNAL_H
 #define _INTERNAL_H
 
-#include <linux/key.h>
+#include <linux/key-type.h>
 #include <linux/key-ui.h>
 
-#if 0
-#define kenter(FMT, a...)      printk("==> %s("FMT")\n",__FUNCTION__ , ## a)
-#define kleave(FMT, a...)      printk("<== %s()"FMT"\n",__FUNCTION__ , ## a)
-#define kdebug(FMT, a...)      printk(FMT"\n" , ## a)
+static inline __attribute__((format(printf, 1, 2)))
+void no_printk(const char *fmt, ...)
+{
+}
+
+#ifdef __KDEBUG
+#define kenter(FMT, ...) \
+       printk(KERN_DEBUG "==> %s("FMT")\n", __FUNCTION__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+       printk(KERN_DEBUG "<== %s()"FMT"\n", __FUNCTION__, ##__VA_ARGS__)
+#define kdebug(FMT, ...) \
+       printk(KERN_DEBUG "xxx" FMT"yyy\n", ##__VA_ARGS__)
 #else
-#define kenter(FMT, a...)      do {} while(0)
-#define kleave(FMT, a...)      do {} while(0)
-#define kdebug(FMT, a...)      do {} while(0)
+#define kenter(FMT, ...) \
+       no_printk(KERN_DEBUG "==> %s("FMT")\n", __FUNCTION__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+       no_printk(KERN_DEBUG "<== %s()"FMT"\n", __FUNCTION__, ##__VA_ARGS__)
+#define kdebug(FMT, ...) \
+       no_printk(KERN_DEBUG FMT"\n", ##__VA_ARGS__)
 #endif
 
 extern struct key_type key_type_user;
  */
 struct key_user {
        struct rb_node          node;
-       struct list_head        consq;          /* construction queue */
+       struct mutex            cons_lock;      /* construction initiation lock */
        spinlock_t              lock;
        atomic_t                usage;          /* for accessing qnkeys & qnbytes */
        atomic_t                nkeys;          /* number of keys */
 extern struct rb_root key_serial_tree;
 extern spinlock_t key_serial_lock;
 extern struct semaphore key_alloc_sem;
-extern struct rw_semaphore key_construction_sem;
+extern struct mutex key_construction_mutex;
 extern wait_queue_head_t request_key_conswq;
 
 
 struct request_key_auth {
        struct key              *target_key;
        struct task_struct      *context;
-       const char              *callout_info;
+       char                    *callout_info;
        pid_t                   pid;
 };
 
 
-/* key.c: basic authentication token and access key management
+/* Basic authentication token and access key management
  *
- * Copyright (C) 2004-6 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
 static DECLARE_WORK(key_cleanup_task, key_cleanup);
 
 /* we serialise key instantiation and link */
-DECLARE_RWSEM(key_construction_sem);
+DEFINE_MUTEX(key_construction_mutex);
 
 /* any key who's type gets unegistered will be re-typed to this */
 static struct key_type key_type_dead = {
        candidate->qnkeys = 0;
        candidate->qnbytes = 0;
        spin_lock_init(&candidate->lock);
-       INIT_LIST_HEAD(&candidate->consq);
+       mutex_init(&candidate->cons_lock);
 
        rb_link_node(&candidate->node, parent, p);
        rb_insert_color(&candidate->node, &key_user_tree);
        awaken = 0;
        ret = -EBUSY;
 
-       down_write(&key_construction_sem);
+       mutex_lock(&key_construction_mutex);
 
        /* can't instantiate twice */
        if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
                }
        }
 
-       up_write(&key_construction_sem);
+       mutex_unlock(&key_construction_mutex);
 
        /* wake up anyone waiting for a key to be constructed */
        if (awaken)
-               wake_up_all(&request_key_conswq);
+               wake_up_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT);
 
        return ret;
 
        if (keyring)
                down_write(&keyring->sem);
 
-       down_write(&key_construction_sem);
+       mutex_lock(&key_construction_mutex);
 
        /* can't instantiate twice */
        if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
                        key_revoke(instkey);
        }
 
-       up_write(&key_construction_sem);
+       mutex_unlock(&key_construction_mutex);
 
        if (keyring)
                up_write(&keyring->sem);
 
        /* wake up anyone waiting for a key to be constructed */
        if (awaken)
-               wake_up_all(&request_key_conswq);
+               wake_up_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT);
 
        return ret;
 
 {
        key_check(key);
 
-       /* make sure no one's trying to change or use the key when we mark
-        * it */
-       down_write(&key->sem);
-       set_bit(KEY_FLAG_REVOKED, &key->flags);
-
-       if (key->type->revoke)
+       /* make sure no one's trying to change or use the key when we mark it
+        * - we tell lockdep that we might nest because we might be revoking an
+        *   authorisation key whilst holding the sem on a key we've just
+        *   instantiated
+        */
+       down_write_nested(&key->sem, 1);
+       if (!test_and_set_bit(KEY_FLAG_REVOKED, &key->flags) &&
+           key->type->revoke)
                key->type->revoke(key);
 
        up_write(&key->sem);
 
 /* the root user's tracking struct */
 struct key_user root_key_user = {
        .usage          = ATOMIC_INIT(3),
-       .consq          = LIST_HEAD_INIT(root_key_user.consq),
+       .cons_lock      = __MUTEX_INITIALIZER(root_key_user.cons_lock),
        .lock           = __SPIN_LOCK_UNLOCKED(root_key_user.lock),
        .nkeys          = ATOMIC_INIT(2),
        .nikeys         = ATOMIC_INIT(2),
                break;
        }
 
-       /* check the status */
-       if (perm) {
+       if (!partial) {
+               ret = wait_for_key_construction(key, true);
+               switch (ret) {
+               case -ERESTARTSYS:
+                       goto invalid_key;
+               default:
+                       if (perm)
+                               goto invalid_key;
+               case 0:
+                       break;
+               }
+       } else if (perm) {
                ret = key_validate(key);
                if (ret < 0)
                        goto invalid_key;
 
-/* request_key.c: request a key from userspace
+/* Request a key from userspace
  *
- * Copyright (C) 2004-6 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
 #include <linux/keyctl.h>
 #include "internal.h"
 
-struct key_construction {
-       struct list_head        link;   /* link in construction queue */
-       struct key              *key;   /* key being constructed */
-};
+/*
+ * wait_on_bit() sleep function for uninterruptible waiting
+ */
+static int key_wait_bit(void *flags)
+{
+       schedule();
+       return 0;
+}
+
+/*
+ * wait_on_bit() sleep function for interruptible waiting
+ */
+static int key_wait_bit_intr(void *flags)
+{
+       schedule();
+       return signal_pending(current) ? -ERESTARTSYS : 0;
+}
+
+/*
+ * call to complete the construction of a key
+ */
+void complete_request_key(struct key_construction *cons, int error)
+{
+       kenter("{%d,%d},%d", cons->key->serial, cons->authkey->serial, error);
 
-/* when waiting for someone else's keys, you get added to this */
-DECLARE_WAIT_QUEUE_HEAD(request_key_conswq);
+       if (error < 0)
+               key_negate_and_link(cons->key, key_negative_timeout, NULL,
+                                   cons->authkey);
+       else
+               key_revoke(cons->authkey);
+
+       key_put(cons->key);
+       key_put(cons->authkey);
+       kfree(cons);
+}
+EXPORT_SYMBOL(complete_request_key);
 
-/*****************************************************************************/
 /*
  * request userspace finish the construction of a key
  * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring>"
  */
-static int call_sbin_request_key(struct key *key,
-                                struct key *authkey,
+static int call_sbin_request_key(struct key_construction *cons,
                                 const char *op,
                                 void *aux)
 {
        struct task_struct *tsk = current;
        key_serial_t prkey, sskey;
-       struct key *keyring;
+       struct key *key = cons->key, *authkey = cons->authkey, *keyring;
        char *argv[9], *envp[3], uid_str[12], gid_str[12];
        char key_str[12], keyring_str[3][12];
        char desc[20];
                rcu_read_lock();
                sskey = rcu_dereference(tsk->signal->session_keyring)->serial;
                rcu_read_unlock();
-       }
-       else {
+       } else {
                sskey = tsk->user->session_keyring->serial;
        }
 
        /* do it */
        ret = call_usermodehelper_keys(argv[0], argv, envp, keyring,
                                       UMH_WAIT_PROC);
+       kdebug("usermode -> 0x%x", ret);
+       if (ret >= 0) {
+               /* ret is the exit/wait code */
+               if (test_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags) ||
+                   key_validate(key) < 0)
+                       ret = -ENOKEY;
+               else
+                       /* ignore any errors from userspace if the key was
+                        * instantiated */
+                       ret = 0;
+       }
 
 error_link:
        key_put(keyring);
 
 error_alloc:
        kleave(" = %d", ret);
+       complete_request_key(cons, ret);
        return ret;
+}
 
-} /* end call_sbin_request_key() */
-
-/*****************************************************************************/
 /*
- * call out to userspace for the key
- * - called with the construction sem held, but the sem is dropped here
+ * call out to userspace for key construction
  * - we ignore program failure and go on key status instead
  */
-static struct key *__request_key_construction(struct key_type *type,
-                                             const char *description,
-                                             const char *callout_info,
-                                             void *aux,
-                                             unsigned long flags)
+static int construct_key(struct key *key, const char *callout_info, void *aux)
 {
+       struct key_construction *cons;
        request_key_actor_t actor;
-       struct key_construction cons;
-       struct timespec now;
-       struct key *key, *authkey;
-       int ret, negated;
+       struct key *authkey;
+       int ret;
 
-       kenter("%s,%s,%s,%lx", type->name, description, callout_info, flags);
+       kenter("%d,%s,%p", key->serial, callout_info, aux);
 
-       /* create a key and add it to the queue */
-       key = key_alloc(type, description,
-                       current->fsuid, current->fsgid, current, KEY_POS_ALL,
-                       flags);
-       if (IS_ERR(key))
-               goto alloc_failed;
-
-       set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
-
-       cons.key = key;
-       list_add_tail(&cons.link, &key->user->consq);
-
-       /* we drop the construction sem here on behalf of the caller */
-       up_write(&key_construction_sem);
+       cons = kmalloc(sizeof(*cons), GFP_KERNEL);
+       if (!cons)
+               return -ENOMEM;
 
        /* allocate an authorisation key */
        authkey = request_key_auth_new(key, callout_info);
        if (IS_ERR(authkey)) {
+               kfree(cons);
                ret = PTR_ERR(authkey);
                authkey = NULL;
-               goto alloc_authkey_failed;
-       }
-
-       /* make the call */
-       actor = call_sbin_request_key;
-       if (type->request_key)
-               actor = type->request_key;
-       ret = actor(key, authkey, "create", aux);
-       if (ret < 0)
-               goto request_failed;
-
-       /* if the key wasn't instantiated, then we want to give an error */
-       ret = -ENOKEY;
-       if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
-               goto request_failed;
-
-       key_revoke(authkey);
-       key_put(authkey);
-
-       down_write(&key_construction_sem);
-       list_del(&cons.link);
-       up_write(&key_construction_sem);
-
-       /* also give an error if the key was negatively instantiated */
-check_not_negative:
-       if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) {
-               key_put(key);
-               key = ERR_PTR(-ENOKEY);
-       }
-
-out:
-       kleave(" = %p", key);
-       return key;
-
-request_failed:
-       key_revoke(authkey);
-       key_put(authkey);
-
-alloc_authkey_failed:
-       /* it wasn't instantiated
-        * - remove from construction queue
-        * - mark the key as dead
-        */
-       negated = 0;
-       down_write(&key_construction_sem);
-
-       list_del(&cons.link);
-
-       /* check it didn't get instantiated between the check and the down */
-       if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
-               set_bit(KEY_FLAG_NEGATIVE, &key->flags);
-               set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
-               negated = 1;
-       }
-
-       clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
-
-       up_write(&key_construction_sem);
-
-       if (!negated)
-               goto check_not_negative; /* surprisingly, the key got
-                                         * instantiated */
-
-       /* set the timeout and store in the session keyring if we can */
-       now = current_kernel_time();
-       key->expiry = now.tv_sec + key_negative_timeout;
-
-       if (current->signal->session_keyring) {
-               struct key *keyring;
-
-               rcu_read_lock();
-               keyring = rcu_dereference(current->signal->session_keyring);
-               atomic_inc(&keyring->usage);
-               rcu_read_unlock();
-
-               key_link(keyring, key);
-               key_put(keyring);
-       }
-
-       key_put(key);
-
-       /* notify anyone who was waiting */
-       wake_up_all(&request_key_conswq);
-
-       key = ERR_PTR(ret);
-       goto out;
-
-alloc_failed:
-       up_write(&key_construction_sem);
-       goto out;
-
-} /* end __request_key_construction() */
-
-/*****************************************************************************/
-/*
- * call out to userspace to request the key
- * - we check the construction queue first to see if an appropriate key is
- *   already being constructed by userspace
- */
-static struct key *request_key_construction(struct key_type *type,
-                                           const char *description,
-                                           const char *callout_info,
-                                           void *aux,
-                                           struct key_user *user,
-                                           unsigned long flags)
-{
-       struct key_construction *pcons;
-       struct key *key, *ckey;
-
-       DECLARE_WAITQUEUE(myself, current);
-
-       kenter("%s,%s,{%d},%s,%lx",
-              type->name, description, user->uid, callout_info, flags);
-
-       /* see if there's such a key under construction already */
-       down_write(&key_construction_sem);
-
-       list_for_each_entry(pcons, &user->consq, link) {
-               ckey = pcons->key;
-
-               if (ckey->type != type)
-                       continue;
-
-               if (type->match(ckey, description))
-                       goto found_key_under_construction;
+       } else {
+               cons->authkey = key_get(authkey);
+               cons->key = key_get(key);
+
+               /* make the call */
+               actor = call_sbin_request_key;
+               if (key->type->request_key)
+                       actor = key->type->request_key;
+
+               ret = actor(cons, "create", aux);
+
+               /* check that the actor called complete_request_key() prior to
+                * returning an error */
+               WARN_ON(ret < 0 &&
+                       !test_bit(KEY_FLAG_REVOKED, &authkey->flags));
+               key_put(authkey);
        }
 
-       /* see about getting userspace to construct the key */
-       key = __request_key_construction(type, description, callout_info, aux,
-                                        flags);
- error:
-       kleave(" = %p", key);
-       return key;
-
-       /* someone else has the same key under construction
-        * - we want to keep an eye on their key
-        */
- found_key_under_construction:
-       atomic_inc(&ckey->usage);
-       up_write(&key_construction_sem);
-
-       /* wait for the key to be completed one way or another */
-       add_wait_queue(&request_key_conswq, &myself);
-
-       for (;;) {
-               set_current_state(TASK_INTERRUPTIBLE);
-               if (!test_bit(KEY_FLAG_USER_CONSTRUCT, &ckey->flags))
-                       break;
-               if (signal_pending(current))
-                       break;
-               schedule();
-       }
-
-       set_current_state(TASK_RUNNING);
-       remove_wait_queue(&request_key_conswq, &myself);
-
-       /* we'll need to search this process's keyrings to see if the key is
-        * now there since we can't automatically assume it's also available
-        * there */
-       key_put(ckey);
-       ckey = NULL;
-
-       key = NULL; /* request a retry */
-       goto error;
-
-} /* end request_key_construction() */
+       kleave(" = %d", ret);
+       return ret;
+}
 
-/*****************************************************************************/
 /*
- * link a freshly minted key to an appropriate destination keyring
+ * link a key to the appropriate destination keyring
+ * - the caller must hold a write lock on the destination keyring
  */
-static void request_key_link(struct key *key, struct key *dest_keyring)
+static void construct_key_make_link(struct key *key, struct key *dest_keyring)
 {
        struct task_struct *tsk = current;
        struct key *drop = NULL;
                                break;
 
                case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
-                       dest_keyring = current->user->session_keyring;
+                       dest_keyring = tsk->user->session_keyring;
                        break;
 
                case KEY_REQKEY_DEFL_USER_KEYRING:
-                       dest_keyring = current->user->uid_keyring;
+                       dest_keyring = tsk->user->uid_keyring;
                        break;
 
                case KEY_REQKEY_DEFL_GROUP_KEYRING:
        }
 
        /* and attach the key to it */
-       key_link(dest_keyring, key);
-
+       __key_link(dest_keyring, key);
        key_put(drop);
-
        kleave("");
+}
 
-} /* end request_key_link() */
+/*
+ * allocate a new key in under-construction state and attempt to link it in to
+ * the requested place
+ * - may return a key that's already under construction instead
+ */
+static int construct_alloc_key(struct key_type *type,
+                              const char *description,
+                              struct key *dest_keyring,
+                              unsigned long flags,
+                              struct key_user *user,
+                              struct key **_key)
+{
+       struct key *key;
+       key_ref_t key_ref;
+
+       kenter("%s,%s,,,", type->name, description);
+
+       mutex_lock(&user->cons_lock);
+
+       key = key_alloc(type, description,
+                       current->fsuid, current->fsgid, current, KEY_POS_ALL,
+                       flags);
+       if (IS_ERR(key))
+               goto alloc_failed;
+
+       set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
+
+       if (dest_keyring)
+               down_write(&dest_keyring->sem);
+
+       /* attach the key to the destination keyring under lock, but we do need
+        * to do another check just in case someone beat us to it whilst we
+        * waited for locks */
+       mutex_lock(&key_construction_mutex);
+
+       key_ref = search_process_keyrings(type, description, type->match,
+                                         current);
+       if (!IS_ERR(key_ref))
+               goto key_already_present;
+
+       if (dest_keyring)
+               construct_key_make_link(key, dest_keyring);
+
+       mutex_unlock(&key_construction_mutex);
+       if (dest_keyring)
+               up_write(&dest_keyring->sem);
+       mutex_unlock(&user->cons_lock);
+       *_key = key;
+       kleave(" = 0 [%d]", key_serial(key));
+       return 0;
+
+key_already_present:
+       mutex_unlock(&key_construction_mutex);
+       if (dest_keyring)
+               up_write(&dest_keyring->sem);
+       mutex_unlock(&user->cons_lock);
+       key_put(key);
+       *_key = key = key_ref_to_ptr(key_ref);
+       kleave(" = -EINPROGRESS [%d]", key_serial(key));
+       return -EINPROGRESS;
+
+alloc_failed:
+       mutex_unlock(&user->cons_lock);
+       *_key = NULL;
+       kleave(" = %ld", PTR_ERR(key));
+       return PTR_ERR(key);
+}
+
+/*
+ * commence key construction
+ */
+static struct key *construct_key_and_link(struct key_type *type,
+                                         const char *description,
+                                         const char *callout_info,
+                                         void *aux,
+                                         struct key *dest_keyring,
+                                         unsigned long flags)
+{
+       struct key_user *user;
+       struct key *key;
+       int ret;
+
+       user = key_user_lookup(current->fsuid);
+       if (!user)
+               return ERR_PTR(-ENOMEM);
+
+       ret = construct_alloc_key(type, description, dest_keyring, flags, user,
+                                 &key);
+       key_user_put(user);
+
+       if (ret == 0) {
+               ret = construct_key(key, callout_info, aux);
+               if (ret < 0)
+                       goto construction_failed;
+       }
+
+       return key;
+
+construction_failed:
+       key_negate_and_link(key, key_negative_timeout, NULL, NULL);
+       key_put(key);
+       return ERR_PTR(ret);
+}
 
-/*****************************************************************************/
 /*
  * request a key
  * - search the process's keyrings
                                 struct key *dest_keyring,
                                 unsigned long flags)
 {
-       struct key_user *user;
        struct key *key;
        key_ref_t key_ref;
 
        key_ref = search_process_keyrings(type, description, type->match,
                                          current);
 
-       kdebug("search 1: %p", key_ref);
-
        if (!IS_ERR(key_ref)) {
                key = key_ref_to_ptr(key_ref);
-       }
-       else if (PTR_ERR(key_ref) != -EAGAIN) {
+       } else if (PTR_ERR(key_ref) != -EAGAIN) {
                key = ERR_PTR(PTR_ERR(key_ref));
-       }
-       else  {
+       } else  {
                /* the search failed, but the keyrings were searchable, so we
                 * should consult userspace if we can */
                key = ERR_PTR(-ENOKEY);
                if (!callout_info)
                        goto error;
 
-               /* - get hold of the user's construction queue */
-               user = key_user_lookup(current->fsuid);
-               if (!user)
-                       goto nomem;
-
-               for (;;) {
-                       if (signal_pending(current))
-                               goto interrupted;
-
-                       /* ask userspace (returns NULL if it waited on a key
-                        * being constructed) */
-                       key = request_key_construction(type, description,
-                                                      callout_info, aux,
-                                                      user, flags);
-                       if (key)
-                               break;
-
-                       /* someone else made the key we want, so we need to
-                        * search again as it might now be available to us */
-                       key_ref = search_process_keyrings(type, description,
-                                                         type->match,
-                                                         current);
-
-                       kdebug("search 2: %p", key_ref);
-
-                       if (!IS_ERR(key_ref)) {
-                               key = key_ref_to_ptr(key_ref);
-                               break;
-                       }
-
-                       if (PTR_ERR(key_ref) != -EAGAIN) {
-                               key = ERR_PTR(PTR_ERR(key_ref));
-                               break;
-                       }
-               }
-
-               key_user_put(user);
-
-               /* link the new key into the appropriate keyring */
-               if (!IS_ERR(key))
-                       request_key_link(key, dest_keyring);
+               key = construct_key_and_link(type, description, callout_info,
+                                            aux, dest_keyring, flags);
        }
 
 error:
        kleave(" = %p", key);
        return key;
+}
 
-nomem:
-       key = ERR_PTR(-ENOMEM);
-       goto error;
-
-interrupted:
-       key_user_put(user);
-       key = ERR_PTR(-EINTR);
-       goto error;
+/*
+ * wait for construction of a key to complete
+ */
+int wait_for_key_construction(struct key *key, bool intr)
+{
+       int ret;
 
-} /* end request_key_and_link() */
+       ret = wait_on_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT,
+                         intr ? key_wait_bit_intr : key_wait_bit,
+                         intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+       if (ret < 0)
+               return ret;
+       return key_validate(key);
+}
+EXPORT_SYMBOL(wait_for_key_construction);
 
-/*****************************************************************************/
 /*
  * request a key
  * - search the process's keyrings
  * - check the list of keys being created or updated
  * - call out to userspace for a key if supplementary info was provided
+ * - waits uninterruptible for creation to complete
  */
 struct key *request_key(struct key_type *type,
                        const char *description,
                        const char *callout_info)
 {
-       return request_key_and_link(type, description, callout_info, NULL,
-                                   NULL, KEY_ALLOC_IN_QUOTA);
-
-} /* end request_key() */
-
+       struct key *key;
+       int ret;
+
+       key = request_key_and_link(type, description, callout_info, NULL,
+                                  NULL, KEY_ALLOC_IN_QUOTA);
+       if (!IS_ERR(key)) {
+               ret = wait_for_key_construction(key, false);
+               if (ret < 0) {
+                       key_put(key);
+                       return ERR_PTR(ret);
+               }
+       }
+       return key;
+}
 EXPORT_SYMBOL(request_key);
 
-/*****************************************************************************/
 /*
  * request a key with auxiliary data for the upcaller
  * - search the process's keyrings
  * - check the list of keys being created or updated
  * - call out to userspace for a key if supplementary info was provided
+ * - waits uninterruptible for creation to complete
  */
 struct key *request_key_with_auxdata(struct key_type *type,
                                     const char *description,
                                     const char *callout_info,
                                     void *aux)
 {
-       return request_key_and_link(type, description, callout_info, aux,
-                                   NULL, KEY_ALLOC_IN_QUOTA);
+       struct key *key;
+       int ret;
+
+       key = request_key_and_link(type, description, callout_info, aux,
+                                  NULL, KEY_ALLOC_IN_QUOTA);
+       if (!IS_ERR(key)) {
+               ret = wait_for_key_construction(key, false);
+               if (ret < 0) {
+                       key_put(key);
+                       return ERR_PTR(ret);
+               }
+       }
+       return key;
+}
+EXPORT_SYMBOL(request_key_with_auxdata);
 
-} /* end request_key_with_auxdata() */
+/*
+ * request a key (allow async construction)
+ * - search the process's keyrings
+ * - check the list of keys being created or updated
+ * - call out to userspace for a key if supplementary info was provided
+ */
+struct key *request_key_async(struct key_type *type,
+                             const char *description,
+                             const char *callout_info)
+{
+       return request_key_and_link(type, description, callout_info, NULL,
+                                   NULL, KEY_ALLOC_IN_QUOTA);
+}
+EXPORT_SYMBOL(request_key_async);
 
-EXPORT_SYMBOL(request_key_with_auxdata);
+/*
+ * request a key with auxiliary data for the upcaller (allow async construction)
+ * - search the process's keyrings
+ * - check the list of keys being created or updated
+ * - call out to userspace for a key if supplementary info was provided
+ */
+struct key *request_key_async_with_auxdata(struct key_type *type,
+                                          const char *description,
+                                          const char *callout_info,
+                                          void *aux)
+{
+       return request_key_and_link(type, description, callout_info, aux,
+                                   NULL, KEY_ALLOC_IN_QUOTA);
+}
+EXPORT_SYMBOL(request_key_async_with_auxdata);
 
        }
 
        key_put(rka->target_key);
+       kfree(rka->callout_info);
        kfree(rka);
 
 } /* end request_key_auth_destroy() */
                kleave(" = -ENOMEM");
                return ERR_PTR(-ENOMEM);
        }
+       rka->callout_info = kmalloc(strlen(callout_info) + 1, GFP_KERNEL);
+       if (!rka->callout_info) {
+               kleave(" = -ENOMEM");
+               kfree(rka);
+               return ERR_PTR(-ENOMEM);
+       }
 
        /* see if the calling process is already servicing the key request of
         * another process */
        }
 
        rka->target_key = key_get(target);
-       rka->callout_info = callout_info;
+       strcpy(rka->callout_info, callout_info);
 
        /* allocate the auth key */
        sprintf(desc, "%x", target->serial);
 
 auth_key_revoked:
        up_read(¤t->request_key_auth->sem);
+       kfree(rka->callout_info);
        kfree(rka);
        kleave("= -EKEYREVOKED");
        return ERR_PTR(-EKEYREVOKED);
        key_put(authkey);
 error_alloc:
        key_put(rka->target_key);
+       kfree(rka->callout_info);
        kfree(rka);
        kleave("= %d", ret);
        return ERR_PTR(ret);