Josh Boyer 9cb73b
From 96dcf8e91389e509021448ffd798cc68471fcf0f Mon Sep 17 00:00:00 2001
Josh Boyer 9cb73b
From: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
Date: Fri, 30 Aug 2013 15:37:50 +0100
Josh Boyer 9cb73b
Subject: [PATCH 01/10] KEYS: Skip key state checks when checking for
Josh Boyer 9cb73b
 possession
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Skip key state checks (invalidation, revocation and expiration) when checking
Josh Boyer 9cb73b
for possession.  Without this, keys that have been marked invalid, revoked
Josh Boyer 9cb73b
keys and expired keys are not given a possession attribute - which means the
Josh Boyer 9cb73b
possessor is not granted any possession permits and cannot do anything with
Josh Boyer 9cb73b
them unless they also have one a user, group or other permit.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
This causes failures in the keyutils test suite's revocation and expiration
Josh Boyer 9cb73b
tests now that commit 96b5c8fea6c0861621051290d705ec2e971963f1 reduced the
Josh Boyer 9cb73b
initial permissions granted to a key.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
The failures are due to accesses to revoked and expired keys being given
Josh Boyer 9cb73b
EACCES instead of EKEYREVOKED or EKEYEXPIRED.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Signed-off-by: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
---
Josh Boyer 9cb73b
 security/keys/internal.h         | 1 +
Josh Boyer 9cb73b
 security/keys/process_keys.c     | 8 +++++---
Josh Boyer 9cb73b
 security/keys/request_key.c      | 6 ++++--
Josh Boyer 9cb73b
 security/keys/request_key_auth.c | 2 +-
Josh Boyer 9cb73b
 4 files changed, 11 insertions(+), 6 deletions(-)
Josh Boyer 9cb73b
Josh Boyer 9cb73b
diff --git a/security/keys/internal.h b/security/keys/internal.h
Josh Boyer 9cb73b
index d4f1468..df971fe 100644
Josh Boyer 9cb73b
--- a/security/keys/internal.h
Josh Boyer 9cb73b
+++ b/security/keys/internal.h
Josh Boyer 9cb73b
@@ -124,6 +124,7 @@ extern key_ref_t search_my_process_keyrings(struct key_type *type,
Josh Boyer 9cb73b
 extern key_ref_t search_process_keyrings(struct key_type *type,
Josh Boyer 9cb73b
 					 const void *description,
Josh Boyer 9cb73b
 					 key_match_func_t match,
Josh Boyer 9cb73b
+					 bool no_state_check,
Josh Boyer 9cb73b
 					 const struct cred *cred);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check);
Josh Boyer 9cb73b
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
Josh Boyer 9cb73b
index 42defae..a3410d6 100644
Josh Boyer 9cb73b
--- a/security/keys/process_keys.c
Josh Boyer 9cb73b
+++ b/security/keys/process_keys.c
Josh Boyer 9cb73b
@@ -440,6 +440,7 @@ found:
Josh Boyer 9cb73b
 key_ref_t search_process_keyrings(struct key_type *type,
Josh Boyer 9cb73b
 				  const void *description,
Josh Boyer 9cb73b
 				  key_match_func_t match,
Josh Boyer 9cb73b
+				  bool no_state_check,
Josh Boyer 9cb73b
 				  const struct cred *cred)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
 	struct request_key_auth *rka;
Josh Boyer 9cb73b
@@ -448,7 +449,7 @@ key_ref_t search_process_keyrings(struct key_type *type,
Josh Boyer 9cb73b
 	might_sleep();
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	key_ref = search_my_process_keyrings(type, description, match,
Josh Boyer 9cb73b
-					     false, cred);
Josh Boyer 9cb73b
+					     no_state_check, cred);
Josh Boyer 9cb73b
 	if (!IS_ERR(key_ref))
Josh Boyer 9cb73b
 		goto found;
Josh Boyer 9cb73b
 	err = key_ref;
Josh Boyer 9cb73b
@@ -468,7 +469,8 @@ key_ref_t search_process_keyrings(struct key_type *type,
Josh Boyer 9cb73b
 			rka = cred->request_key_auth->payload.data;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 			key_ref = search_process_keyrings(type, description,
Josh Boyer 9cb73b
-							  match, rka->cred);
Josh Boyer 9cb73b
+							  match, no_state_check,
Josh Boyer 9cb73b
+							  rka->cred);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 			up_read(&cred->request_key_auth->sem);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -675,7 +677,7 @@ try_again:
Josh Boyer 9cb73b
 		/* check to see if we possess the key */
Josh Boyer 9cb73b
 		skey_ref = search_process_keyrings(key->type, key,
Josh Boyer 9cb73b
 						   lookup_user_key_possessed,
Josh Boyer 9cb73b
-						   cred);
Josh Boyer 9cb73b
+						   true, cred);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 		if (!IS_ERR(skey_ref)) {
Josh Boyer 9cb73b
 			key_put(key);
Josh Boyer 9cb73b
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
Josh Boyer 9cb73b
index c411f9b..172115b 100644
Josh Boyer 9cb73b
--- a/security/keys/request_key.c
Josh Boyer 9cb73b
+++ b/security/keys/request_key.c
Josh Boyer 9cb73b
@@ -390,7 +390,8 @@ static int construct_alloc_key(struct key_type *type,
Josh Boyer 9cb73b
 	 * waited for locks */
Josh Boyer 9cb73b
 	mutex_lock(&key_construction_mutex);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	key_ref = search_process_keyrings(type, description, type->match, cred);
Josh Boyer 9cb73b
+	key_ref = search_process_keyrings(type, description, type->match,
Josh Boyer 9cb73b
+					  false, cred);
Josh Boyer 9cb73b
 	if (!IS_ERR(key_ref))
Josh Boyer 9cb73b
 		goto key_already_present;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -539,7 +540,8 @@ struct key *request_key_and_link(struct key_type *type,
Josh Boyer 9cb73b
 	       dest_keyring, flags);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	/* search all the process keyrings for a key */
Josh Boyer 9cb73b
-	key_ref = search_process_keyrings(type, description, type->match, cred);
Josh Boyer 9cb73b
+	key_ref = search_process_keyrings(type, description, type->match,
Josh Boyer 9cb73b
+					  false, cred);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	if (!IS_ERR(key_ref)) {
Josh Boyer 9cb73b
 		key = key_ref_to_ptr(key_ref);
Josh Boyer 9cb73b
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
Josh Boyer 9cb73b
index 85730d5..92077de 100644
Josh Boyer 9cb73b
--- a/security/keys/request_key_auth.c
Josh Boyer 9cb73b
+++ b/security/keys/request_key_auth.c
Josh Boyer 9cb73b
@@ -247,7 +247,7 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id)
Josh Boyer 9cb73b
 		&key_type_request_key_auth,
Josh Boyer 9cb73b
 		(void *) (unsigned long) target_id,
Josh Boyer 9cb73b
 		key_get_instantiation_authkey_match,
Josh Boyer 9cb73b
-		cred);
Josh Boyer 9cb73b
+		false, cred);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	if (IS_ERR(authkey_ref)) {
Josh Boyer 9cb73b
 		authkey = ERR_CAST(authkey_ref);
Josh Boyer 9cb73b
-- 
Josh Boyer 9cb73b
1.8.3.1
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Josh Boyer 9cb73b
From 9b1294158dd1fbca78541b5d55c057e46b1a9ca2 Mon Sep 17 00:00:00 2001
Josh Boyer 9cb73b
From: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
Date: Fri, 30 Aug 2013 15:37:51 +0100
Josh Boyer 9cb73b
Subject: [PATCH 02/10] KEYS: Use bool in make_key_ref() and is_key_possessed()
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Make make_key_ref() take a bool possession parameter and make
Josh Boyer 9cb73b
is_key_possessed() return a bool.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Signed-off-by: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
---
Josh Boyer 9cb73b
 Documentation/security/keys.txt | 7 +++----
Josh Boyer 9cb73b
 include/linux/key.h             | 4 ++--
Josh Boyer 9cb73b
 security/keys/keyring.c         | 5 +++--
Josh Boyer 9cb73b
 3 files changed, 8 insertions(+), 8 deletions(-)
Josh Boyer 9cb73b
Josh Boyer 9cb73b
diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt
Josh Boyer 9cb73b
index 7b4145d..9ede670 100644
Josh Boyer 9cb73b
--- a/Documentation/security/keys.txt
Josh Boyer 9cb73b
+++ b/Documentation/security/keys.txt
Josh Boyer 9cb73b
@@ -865,15 +865,14 @@ encountered:
Josh Boyer 9cb73b
      calling processes has a searchable link to the key from one of its
Josh Boyer 9cb73b
      keyrings. There are three functions for dealing with these:
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	key_ref_t make_key_ref(const struct key *key,
Josh Boyer 9cb73b
-			       unsigned long possession);
Josh Boyer 9cb73b
+	key_ref_t make_key_ref(const struct key *key, bool possession);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	struct key *key_ref_to_ptr(const key_ref_t key_ref);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	unsigned long is_key_possessed(const key_ref_t key_ref);
Josh Boyer 9cb73b
+	bool is_key_possessed(const key_ref_t key_ref);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
      The first function constructs a key reference from a key pointer and
Josh Boyer 9cb73b
-     possession information (which must be 0 or 1 and not any other value).
Josh Boyer 9cb73b
+     possession information (which must be true or false).
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
      The second function retrieves the key pointer from a reference and the
Josh Boyer 9cb73b
      third retrieves the possession flag.
Josh Boyer 9cb73b
diff --git a/include/linux/key.h b/include/linux/key.h
Josh Boyer 9cb73b
index 4dfde11..51bce29 100644
Josh Boyer 9cb73b
--- a/include/linux/key.h
Josh Boyer 9cb73b
+++ b/include/linux/key.h
Josh Boyer 9cb73b
@@ -99,7 +99,7 @@ struct keyring_name;
Josh Boyer 9cb73b
 typedef struct __key_reference_with_attributes *key_ref_t;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 static inline key_ref_t make_key_ref(const struct key *key,
Josh Boyer 9cb73b
-				     unsigned long possession)
Josh Boyer 9cb73b
+				     bool possession)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
 	return (key_ref_t) ((unsigned long) key | possession);
Josh Boyer 9cb73b
 }
Josh Boyer 9cb73b
@@ -109,7 +109,7 @@ static inline struct key *key_ref_to_ptr(const key_ref_t key_ref)
Josh Boyer 9cb73b
 	return (struct key *) ((unsigned long) key_ref & ~1UL);
Josh Boyer 9cb73b
 }
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-static inline unsigned long is_key_possessed(const key_ref_t key_ref)
Josh Boyer 9cb73b
+static inline bool is_key_possessed(const key_ref_t key_ref)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
 	return (unsigned long) key_ref & 1UL;
Josh Boyer 9cb73b
 }
Josh Boyer 9cb73b
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
Josh Boyer 9cb73b
index 6ece7f2..f784063 100644
Josh Boyer 9cb73b
--- a/security/keys/keyring.c
Josh Boyer 9cb73b
+++ b/security/keys/keyring.c
Josh Boyer 9cb73b
@@ -329,9 +329,10 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	struct keyring_list *keylist;
Josh Boyer 9cb73b
 	struct timespec now;
Josh Boyer 9cb73b
-	unsigned long possessed, kflags;
Josh Boyer 9cb73b
+	unsigned long kflags;
Josh Boyer 9cb73b
 	struct key *keyring, *key;
Josh Boyer 9cb73b
 	key_ref_t key_ref;
Josh Boyer 9cb73b
+	bool possessed;
Josh Boyer 9cb73b
 	long err;
Josh Boyer 9cb73b
 	int sp, nkeys, kix;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -542,8 +543,8 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref,
Josh Boyer 9cb73b
 			       key_perm_t perm)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
 	struct keyring_list *klist;
Josh Boyer 9cb73b
-	unsigned long possessed;
Josh Boyer 9cb73b
 	struct key *keyring, *key;
Josh Boyer 9cb73b
+	bool possessed;
Josh Boyer 9cb73b
 	int nkeys, loop;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	keyring = key_ref_to_ptr(keyring_ref);
Josh Boyer 9cb73b
-- 
Josh Boyer 9cb73b
1.8.3.1
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Josh Boyer 9cb73b
From 4a7e7536b9b728f1d912d0e4c047c885c95e13a1 Mon Sep 17 00:00:00 2001
Josh Boyer 9cb73b
From: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
Date: Fri, 30 Aug 2013 15:37:51 +0100
Josh Boyer 9cb73b
Subject: [PATCH 03/10] KEYS: key_is_dead() should take a const key pointer
Josh Boyer 9cb73b
 argument
Josh Boyer 9cb73b
Josh Boyer 9cb73b
key_is_dead() should take a const key pointer argument as it doesn't modify
Josh Boyer 9cb73b
what it points to.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Signed-off-by: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
---
Josh Boyer 9cb73b
 security/keys/internal.h | 2 +-
Josh Boyer 9cb73b
 1 file changed, 1 insertion(+), 1 deletion(-)
Josh Boyer 9cb73b
Josh Boyer 9cb73b
diff --git a/security/keys/internal.h b/security/keys/internal.h
Josh Boyer 9cb73b
index df971fe..490aef5 100644
Josh Boyer 9cb73b
--- a/security/keys/internal.h
Josh Boyer 9cb73b
+++ b/security/keys/internal.h
Josh Boyer 9cb73b
@@ -203,7 +203,7 @@ extern struct key *key_get_instantiation_authkey(key_serial_t target_id);
Josh Boyer 9cb73b
 /*
Josh Boyer 9cb73b
  * Determine whether a key is dead.
Josh Boyer 9cb73b
  */
Josh Boyer 9cb73b
-static inline bool key_is_dead(struct key *key, time_t limit)
Josh Boyer 9cb73b
+static inline bool key_is_dead(const struct key *key, time_t limit)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
 	return
Josh Boyer 9cb73b
 		key->flags & ((1 << KEY_FLAG_DEAD) |
Josh Boyer 9cb73b
-- 
Josh Boyer 9cb73b
1.8.3.1
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Josh Boyer 9cb73b
From 9007a0a7f8c135f0085e46db277de0cf7b944403 Mon Sep 17 00:00:00 2001
Josh Boyer 9cb73b
From: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
Date: Fri, 30 Aug 2013 15:37:52 +0100
Josh Boyer 9cb73b
Subject: [PATCH 04/10] KEYS: Consolidate the concept of an 'index key' for key
Josh Boyer 9cb73b
 access
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Consolidate the concept of an 'index key' for accessing keys.  The index key
Josh Boyer 9cb73b
is the search term needed to find a key directly - basically the key type and
Josh Boyer 9cb73b
the key description.  We can add to that the description length.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
This will be useful when turning a keyring into an associative array rather
Josh Boyer 9cb73b
than just a pointer block.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Signed-off-by: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
---
Josh Boyer 9cb73b
 include/linux/key.h         | 21 +++++++++----
Josh Boyer 9cb73b
 security/keys/internal.h    |  8 ++---
Josh Boyer 9cb73b
 security/keys/key.c         | 72 +++++++++++++++++++++++----------------------
Josh Boyer 9cb73b
 security/keys/keyring.c     | 37 +++++++++++------------
Josh Boyer 9cb73b
 security/keys/request_key.c | 12 +++++---
Josh Boyer 9cb73b
 5 files changed, 83 insertions(+), 67 deletions(-)
Josh Boyer 9cb73b
Josh Boyer 9cb73b
diff --git a/include/linux/key.h b/include/linux/key.h
Josh Boyer 9cb73b
index 51bce29..d573e82 100644
Josh Boyer 9cb73b
--- a/include/linux/key.h
Josh Boyer 9cb73b
+++ b/include/linux/key.h
Josh Boyer 9cb73b
@@ -82,6 +82,12 @@ struct key_owner;
Josh Boyer 9cb73b
 struct keyring_list;
Josh Boyer 9cb73b
 struct keyring_name;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
+struct keyring_index_key {
Josh Boyer 9cb73b
+	struct key_type		*type;
Josh Boyer 9cb73b
+	const char		*description;
Josh Boyer 9cb73b
+	size_t			desc_len;
Josh Boyer 9cb73b
+};
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
 /*****************************************************************************/
Josh Boyer 9cb73b
 /*
Josh Boyer 9cb73b
  * key reference with possession attribute handling
Josh Boyer 9cb73b
@@ -129,7 +135,6 @@ struct key {
Josh Boyer 9cb73b
 		struct list_head graveyard_link;
Josh Boyer 9cb73b
 		struct rb_node	serial_node;
Josh Boyer 9cb73b
 	};
Josh Boyer 9cb73b
-	struct key_type		*type;		/* type of key */
Josh Boyer 9cb73b
 	struct rw_semaphore	sem;		/* change vs change sem */
Josh Boyer 9cb73b
 	struct key_user		*user;		/* owner of this key */
Josh Boyer 9cb73b
 	void			*security;	/* security data for this key */
Josh Boyer 9cb73b
@@ -163,12 +168,18 @@ struct key {
Josh Boyer 9cb73b
 #define KEY_FLAG_ROOT_CAN_CLEAR	6	/* set if key can be cleared by root without permission */
Josh Boyer 9cb73b
 #define KEY_FLAG_INVALIDATED	7	/* set if key has been invalidated */
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	/* the description string
Josh Boyer 9cb73b
-	 * - this is used to match a key against search criteria
Josh Boyer 9cb73b
-	 * - this should be a printable string
Josh Boyer 9cb73b
+	/* the key type and key description string
Josh Boyer 9cb73b
+	 * - the desc is used to match a key against search criteria
Josh Boyer 9cb73b
+	 * - it should be a printable string
Josh Boyer 9cb73b
 	 * - eg: for krb5 AFS, this might be "afs@REDHAT.COM"
Josh Boyer 9cb73b
 	 */
Josh Boyer 9cb73b
-	char			*description;
Josh Boyer 9cb73b
+	union {
Josh Boyer 9cb73b
+		struct keyring_index_key index_key;
Josh Boyer 9cb73b
+		struct {
Josh Boyer 9cb73b
+			struct key_type	*type;		/* type of key */
Josh Boyer 9cb73b
+			char		*description;
Josh Boyer 9cb73b
+		};
Josh Boyer 9cb73b
+	};
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	/* type specific data
Josh Boyer 9cb73b
 	 * - this is used by the keyring type to index the name
Josh Boyer 9cb73b
diff --git a/security/keys/internal.h b/security/keys/internal.h
Josh Boyer 9cb73b
index 490aef5..77441dd 100644
Josh Boyer 9cb73b
--- a/security/keys/internal.h
Josh Boyer 9cb73b
+++ b/security/keys/internal.h
Josh Boyer 9cb73b
@@ -89,19 +89,17 @@ extern struct key_type *key_type_lookup(const char *type);
Josh Boyer 9cb73b
 extern void key_type_put(struct key_type *ktype);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 extern int __key_link_begin(struct key *keyring,
Josh Boyer 9cb73b
-			    const struct key_type *type,
Josh Boyer 9cb73b
-			    const char *description,
Josh Boyer 9cb73b
+			    const struct keyring_index_key *index_key,
Josh Boyer 9cb73b
 			    unsigned long *_prealloc);
Josh Boyer 9cb73b
 extern int __key_link_check_live_key(struct key *keyring, struct key *key);
Josh Boyer 9cb73b
 extern void __key_link(struct key *keyring, struct key *key,
Josh Boyer 9cb73b
 		       unsigned long *_prealloc);
Josh Boyer 9cb73b
 extern void __key_link_end(struct key *keyring,
Josh Boyer 9cb73b
-			   struct key_type *type,
Josh Boyer 9cb73b
+			   const struct keyring_index_key *index_key,
Josh Boyer 9cb73b
 			   unsigned long prealloc);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 extern key_ref_t __keyring_search_one(key_ref_t keyring_ref,
Josh Boyer 9cb73b
-				      const struct key_type *type,
Josh Boyer 9cb73b
-				      const char *description,
Josh Boyer 9cb73b
+				      const struct keyring_index_key *index_key,
Josh Boyer 9cb73b
 				      key_perm_t perm);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 extern struct key *keyring_search_instkey(struct key *keyring,
Josh Boyer 9cb73b
diff --git a/security/keys/key.c b/security/keys/key.c
Josh Boyer 9cb73b
index 8fb7c7b..7e6bc39 100644
Josh Boyer 9cb73b
--- a/security/keys/key.c
Josh Boyer 9cb73b
+++ b/security/keys/key.c
Josh Boyer 9cb73b
@@ -242,8 +242,8 @@ struct key *key_alloc(struct key_type *type, const char *desc,
Josh Boyer 9cb73b
 		}
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	desclen = strlen(desc) + 1;
Josh Boyer 9cb73b
-	quotalen = desclen + type->def_datalen;
Josh Boyer 9cb73b
+	desclen = strlen(desc);
Josh Boyer 9cb73b
+	quotalen = desclen + 1 + type->def_datalen;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	/* get hold of the key tracking for this user */
Josh Boyer 9cb73b
 	user = key_user_lookup(uid);
Josh Boyer 9cb73b
@@ -277,7 +277,8 @@ struct key *key_alloc(struct key_type *type, const char *desc,
Josh Boyer 9cb73b
 		goto no_memory_2;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	if (desc) {
Josh Boyer 9cb73b
-		key->description = kmemdup(desc, desclen, GFP_KERNEL);
Josh Boyer 9cb73b
+		key->index_key.desc_len = desclen;
Josh Boyer 9cb73b
+		key->index_key.description = kmemdup(desc, desclen + 1, GFP_KERNEL);
Josh Boyer 9cb73b
 		if (!key->description)
Josh Boyer 9cb73b
 			goto no_memory_3;
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
@@ -285,7 +286,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
Josh Boyer 9cb73b
 	atomic_set(&key->usage, 1);
Josh Boyer 9cb73b
 	init_rwsem(&key->sem);
Josh Boyer 9cb73b
 	lockdep_set_class(&key->sem, &type->lock_class);
Josh Boyer 9cb73b
-	key->type = type;
Josh Boyer 9cb73b
+	key->index_key.type = type;
Josh Boyer 9cb73b
 	key->user = user;
Josh Boyer 9cb73b
 	key->quotalen = quotalen;
Josh Boyer 9cb73b
 	key->datalen = type->def_datalen;
Josh Boyer 9cb73b
@@ -489,8 +490,7 @@ int key_instantiate_and_link(struct key *key,
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	if (keyring) {
Josh Boyer 9cb73b
-		ret = __key_link_begin(keyring, key->type, key->description,
Josh Boyer 9cb73b
-				       &prealloc);
Josh Boyer 9cb73b
+		ret = __key_link_begin(keyring, &key->index_key, &prealloc);
Josh Boyer 9cb73b
 		if (ret < 0)
Josh Boyer 9cb73b
 			goto error_free_preparse;
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
@@ -499,7 +499,7 @@ int key_instantiate_and_link(struct key *key,
Josh Boyer 9cb73b
 					 &prealloc);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	if (keyring)
Josh Boyer 9cb73b
-		__key_link_end(keyring, key->type, prealloc);
Josh Boyer 9cb73b
+		__key_link_end(keyring, &key->index_key, prealloc);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 error_free_preparse:
Josh Boyer 9cb73b
 	if (key->type->preparse)
Josh Boyer 9cb73b
@@ -548,8 +548,7 @@ int key_reject_and_link(struct key *key,
Josh Boyer 9cb73b
 	ret = -EBUSY;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	if (keyring)
Josh Boyer 9cb73b
-		link_ret = __key_link_begin(keyring, key->type,
Josh Boyer 9cb73b
-					    key->description, &prealloc);
Josh Boyer 9cb73b
+		link_ret = __key_link_begin(keyring, &key->index_key, &prealloc);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	mutex_lock(&key_construction_mutex);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -581,7 +580,7 @@ int key_reject_and_link(struct key *key,
Josh Boyer 9cb73b
 	mutex_unlock(&key_construction_mutex);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	if (keyring)
Josh Boyer 9cb73b
-		__key_link_end(keyring, key->type, prealloc);
Josh Boyer 9cb73b
+		__key_link_end(keyring, &key->index_key, prealloc);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	/* wake up anyone waiting for a key to be constructed */
Josh Boyer 9cb73b
 	if (awaken)
Josh Boyer 9cb73b
@@ -780,25 +779,27 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
Josh Boyer 9cb73b
 			       key_perm_t perm,
Josh Boyer 9cb73b
 			       unsigned long flags)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
-	unsigned long prealloc;
Josh Boyer 9cb73b
+	struct keyring_index_key index_key = {
Josh Boyer 9cb73b
+		.description	= description,
Josh Boyer 9cb73b
+	};
Josh Boyer 9cb73b
 	struct key_preparsed_payload prep;
Josh Boyer 9cb73b
 	const struct cred *cred = current_cred();
Josh Boyer 9cb73b
-	struct key_type *ktype;
Josh Boyer 9cb73b
+	unsigned long prealloc;
Josh Boyer 9cb73b
 	struct key *keyring, *key = NULL;
Josh Boyer 9cb73b
 	key_ref_t key_ref;
Josh Boyer 9cb73b
 	int ret;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	/* look up the key type to see if it's one of the registered kernel
Josh Boyer 9cb73b
 	 * types */
Josh Boyer 9cb73b
-	ktype = key_type_lookup(type);
Josh Boyer 9cb73b
-	if (IS_ERR(ktype)) {
Josh Boyer 9cb73b
+	index_key.type = key_type_lookup(type);
Josh Boyer 9cb73b
+	if (IS_ERR(index_key.type)) {
Josh Boyer 9cb73b
 		key_ref = ERR_PTR(-ENODEV);
Josh Boyer 9cb73b
 		goto error;
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	key_ref = ERR_PTR(-EINVAL);
Josh Boyer 9cb73b
-	if (!ktype->match || !ktype->instantiate ||
Josh Boyer 9cb73b
-	    (!description && !ktype->preparse))
Josh Boyer 9cb73b
+	if (!index_key.type->match || !index_key.type->instantiate ||
Josh Boyer 9cb73b
+	    (!index_key.description && !index_key.type->preparse))
Josh Boyer 9cb73b
 		goto error_put_type;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	keyring = key_ref_to_ptr(keyring_ref);
Josh Boyer 9cb73b
@@ -812,21 +813,22 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
Josh Boyer 9cb73b
 	memset(&prep, 0, sizeof(prep));
Josh Boyer 9cb73b
 	prep.data = payload;
Josh Boyer 9cb73b
 	prep.datalen = plen;
Josh Boyer 9cb73b
-	prep.quotalen = ktype->def_datalen;
Josh Boyer 9cb73b
-	if (ktype->preparse) {
Josh Boyer 9cb73b
-		ret = ktype->preparse(&prep;;
Josh Boyer 9cb73b
+	prep.quotalen = index_key.type->def_datalen;
Josh Boyer 9cb73b
+	if (index_key.type->preparse) {
Josh Boyer 9cb73b
+		ret = index_key.type->preparse(&prep;;
Josh Boyer 9cb73b
 		if (ret < 0) {
Josh Boyer 9cb73b
 			key_ref = ERR_PTR(ret);
Josh Boyer 9cb73b
 			goto error_put_type;
Josh Boyer 9cb73b
 		}
Josh Boyer 9cb73b
-		if (!description)
Josh Boyer 9cb73b
-			description = prep.description;
Josh Boyer 9cb73b
+		if (!index_key.description)
Josh Boyer 9cb73b
+			index_key.description = prep.description;
Josh Boyer 9cb73b
 		key_ref = ERR_PTR(-EINVAL);
Josh Boyer 9cb73b
-		if (!description)
Josh Boyer 9cb73b
+		if (!index_key.description)
Josh Boyer 9cb73b
 			goto error_free_prep;
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
+	index_key.desc_len = strlen(index_key.description);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	ret = __key_link_begin(keyring, ktype, description, &prealloc);
Josh Boyer 9cb73b
+	ret = __key_link_begin(keyring, &index_key, &prealloc);
Josh Boyer 9cb73b
 	if (ret < 0) {
Josh Boyer 9cb73b
 		key_ref = ERR_PTR(ret);
Josh Boyer 9cb73b
 		goto error_free_prep;
Josh Boyer 9cb73b
@@ -844,9 +846,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
Josh Boyer 9cb73b
 	 * key of the same type and description in the destination keyring and
Josh Boyer 9cb73b
 	 * update that instead if possible
Josh Boyer 9cb73b
 	 */
Josh Boyer 9cb73b
-	if (ktype->update) {
Josh Boyer 9cb73b
-		key_ref = __keyring_search_one(keyring_ref, ktype, description,
Josh Boyer 9cb73b
-					       0);
Josh Boyer 9cb73b
+	if (index_key.type->update) {
Josh Boyer 9cb73b
+		key_ref = __keyring_search_one(keyring_ref, &index_key, 0);
Josh Boyer 9cb73b
 		if (!IS_ERR(key_ref))
Josh Boyer 9cb73b
 			goto found_matching_key;
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
@@ -856,16 +857,17 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
Josh Boyer 9cb73b
 		perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;
Josh Boyer 9cb73b
 		perm |= KEY_USR_VIEW;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-		if (ktype->read)
Josh Boyer 9cb73b
+		if (index_key.type->read)
Josh Boyer 9cb73b
 			perm |= KEY_POS_READ;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-		if (ktype == &key_type_keyring || ktype->update)
Josh Boyer 9cb73b
+		if (index_key.type == &key_type_keyring ||
Josh Boyer 9cb73b
+		    index_key.type->update)
Josh Boyer 9cb73b
 			perm |= KEY_POS_WRITE;
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	/* allocate a new key */
Josh Boyer 9cb73b
-	key = key_alloc(ktype, description, cred->fsuid, cred->fsgid, cred,
Josh Boyer 9cb73b
-			perm, flags);
Josh Boyer 9cb73b
+	key = key_alloc(index_key.type, index_key.description,
Josh Boyer 9cb73b
+			cred->fsuid, cred->fsgid, cred, perm, flags);
Josh Boyer 9cb73b
 	if (IS_ERR(key)) {
Josh Boyer 9cb73b
 		key_ref = ERR_CAST(key);
Josh Boyer 9cb73b
 		goto error_link_end;
Josh Boyer 9cb73b
@@ -882,12 +884,12 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
Josh Boyer 9cb73b
 	key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 error_link_end:
Josh Boyer 9cb73b
-	__key_link_end(keyring, ktype, prealloc);
Josh Boyer 9cb73b
+	__key_link_end(keyring, &index_key, prealloc);
Josh Boyer 9cb73b
 error_free_prep:
Josh Boyer 9cb73b
-	if (ktype->preparse)
Josh Boyer 9cb73b
-		ktype->free_preparse(&prep;;
Josh Boyer 9cb73b
+	if (index_key.type->preparse)
Josh Boyer 9cb73b
+		index_key.type->free_preparse(&prep;;
Josh Boyer 9cb73b
 error_put_type:
Josh Boyer 9cb73b
-	key_type_put(ktype);
Josh Boyer 9cb73b
+	key_type_put(index_key.type);
Josh Boyer 9cb73b
 error:
Josh Boyer 9cb73b
 	return key_ref;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -895,7 +897,7 @@ error:
Josh Boyer 9cb73b
 	/* we found a matching key, so we're going to try to update it
Josh Boyer 9cb73b
 	 * - we can drop the locks first as we have the key pinned
Josh Boyer 9cb73b
 	 */
Josh Boyer 9cb73b
-	__key_link_end(keyring, ktype, prealloc);
Josh Boyer 9cb73b
+	__key_link_end(keyring, &index_key, prealloc);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	key_ref = __key_update(key_ref, &prep;;
Josh Boyer 9cb73b
 	goto error_free_prep;
Josh Boyer 9cb73b
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
Josh Boyer 9cb73b
index f784063..c7f59f9 100644
Josh Boyer 9cb73b
--- a/security/keys/keyring.c
Josh Boyer 9cb73b
+++ b/security/keys/keyring.c
Josh Boyer 9cb73b
@@ -538,8 +538,7 @@ EXPORT_SYMBOL(keyring_search);
Josh Boyer 9cb73b
  * to the returned key reference.
Josh Boyer 9cb73b
  */
Josh Boyer 9cb73b
 key_ref_t __keyring_search_one(key_ref_t keyring_ref,
Josh Boyer 9cb73b
-			       const struct key_type *ktype,
Josh Boyer 9cb73b
-			       const char *description,
Josh Boyer 9cb73b
+			       const struct keyring_index_key *index_key,
Josh Boyer 9cb73b
 			       key_perm_t perm)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
 	struct keyring_list *klist;
Josh Boyer 9cb73b
@@ -558,9 +557,9 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref,
Josh Boyer 9cb73b
 		smp_rmb();
Josh Boyer 9cb73b
 		for (loop = 0; loop < nkeys ; loop++) {
Josh Boyer 9cb73b
 			key = rcu_dereference(klist->keys[loop]);
Josh Boyer 9cb73b
-			if (key->type == ktype &&
Josh Boyer 9cb73b
+			if (key->type == index_key->type &&
Josh Boyer 9cb73b
 			    (!key->type->match ||
Josh Boyer 9cb73b
-			     key->type->match(key, description)) &&
Josh Boyer 9cb73b
+			     key->type->match(key, index_key->description)) &&
Josh Boyer 9cb73b
 			    key_permission(make_key_ref(key, possessed),
Josh Boyer 9cb73b
 					   perm) == 0 &&
Josh Boyer 9cb73b
 			    !(key->flags & ((1 << KEY_FLAG_INVALIDATED) |
Josh Boyer 9cb73b
@@ -747,8 +746,8 @@ static void keyring_unlink_rcu_disposal(struct rcu_head *rcu)
Josh Boyer 9cb73b
 /*
Josh Boyer 9cb73b
  * Preallocate memory so that a key can be linked into to a keyring.
Josh Boyer 9cb73b
  */
Josh Boyer 9cb73b
-int __key_link_begin(struct key *keyring, const struct key_type *type,
Josh Boyer 9cb73b
-		     const char *description, unsigned long *_prealloc)
Josh Boyer 9cb73b
+int __key_link_begin(struct key *keyring, const struct keyring_index_key *index_key,
Josh Boyer 9cb73b
+		     unsigned long *_prealloc)
Josh Boyer 9cb73b
 	__acquires(&keyring->sem)
Josh Boyer 9cb73b
 	__acquires(&keyring_serialise_link_sem)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
@@ -759,7 +758,8 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
Josh Boyer 9cb73b
 	size_t size;
Josh Boyer 9cb73b
 	int loop, lru, ret;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	kenter("%d,%s,%s,", key_serial(keyring), type->name, description);
Josh Boyer 9cb73b
+	kenter("%d,%s,%s,",
Josh Boyer 9cb73b
+	       key_serial(keyring), index_key->type->name, index_key->description);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	if (keyring->type != &key_type_keyring)
Josh Boyer 9cb73b
 		return -ENOTDIR;
Josh Boyer 9cb73b
@@ -772,7 +772,7 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	/* serialise link/link calls to prevent parallel calls causing a cycle
Josh Boyer 9cb73b
 	 * when linking two keyring in opposite orders */
Josh Boyer 9cb73b
-	if (type == &key_type_keyring)
Josh Boyer 9cb73b
+	if (index_key->type == &key_type_keyring)
Josh Boyer 9cb73b
 		down_write(&keyring_serialise_link_sem);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	klist = rcu_dereference_locked_keyring(keyring);
Josh Boyer 9cb73b
@@ -784,8 +784,8 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
Josh Boyer 9cb73b
 		for (loop = klist->nkeys - 1; loop >= 0; loop--) {
Josh Boyer 9cb73b
 			struct key *key = rcu_deref_link_locked(klist, loop,
Josh Boyer 9cb73b
 								keyring);
Josh Boyer 9cb73b
-			if (key->type == type &&
Josh Boyer 9cb73b
-			    strcmp(key->description, description) == 0) {
Josh Boyer 9cb73b
+			if (key->type == index_key->type &&
Josh Boyer 9cb73b
+			    strcmp(key->description, index_key->description) == 0) {
Josh Boyer 9cb73b
 				/* Found a match - we'll replace the link with
Josh Boyer 9cb73b
 				 * one to the new key.  We record the slot
Josh Boyer 9cb73b
 				 * position.
Josh Boyer 9cb73b
@@ -865,7 +865,7 @@ error_quota:
Josh Boyer 9cb73b
 	key_payload_reserve(keyring,
Josh Boyer 9cb73b
 			    keyring->datalen - KEYQUOTA_LINK_BYTES);
Josh Boyer 9cb73b
 error_sem:
Josh Boyer 9cb73b
-	if (type == &key_type_keyring)
Josh Boyer 9cb73b
+	if (index_key->type == &key_type_keyring)
Josh Boyer 9cb73b
 		up_write(&keyring_serialise_link_sem);
Josh Boyer 9cb73b
 error_krsem:
Josh Boyer 9cb73b
 	up_write(&keyring->sem);
Josh Boyer 9cb73b
@@ -957,16 +957,17 @@ void __key_link(struct key *keyring, struct key *key,
Josh Boyer 9cb73b
  *
Josh Boyer 9cb73b
  * Must be called with __key_link_begin() having being called.
Josh Boyer 9cb73b
  */
Josh Boyer 9cb73b
-void __key_link_end(struct key *keyring, struct key_type *type,
Josh Boyer 9cb73b
+void __key_link_end(struct key *keyring,
Josh Boyer 9cb73b
+		    const struct keyring_index_key *index_key,
Josh Boyer 9cb73b
 		    unsigned long prealloc)
Josh Boyer 9cb73b
 	__releases(&keyring->sem)
Josh Boyer 9cb73b
 	__releases(&keyring_serialise_link_sem)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
-	BUG_ON(type == NULL);
Josh Boyer 9cb73b
-	BUG_ON(type->name == NULL);
Josh Boyer 9cb73b
-	kenter("%d,%s,%lx", keyring->serial, type->name, prealloc);
Josh Boyer 9cb73b
+	BUG_ON(index_key->type == NULL);
Josh Boyer 9cb73b
+	BUG_ON(index_key->type->name == NULL);
Josh Boyer 9cb73b
+	kenter("%d,%s,%lx", keyring->serial, index_key->type->name, prealloc);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	if (type == &key_type_keyring)
Josh Boyer 9cb73b
+	if (index_key->type == &key_type_keyring)
Josh Boyer 9cb73b
 		up_write(&keyring_serialise_link_sem);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	if (prealloc) {
Josh Boyer 9cb73b
@@ -1007,12 +1008,12 @@ int key_link(struct key *keyring, struct key *key)
Josh Boyer 9cb73b
 	key_check(keyring);
Josh Boyer 9cb73b
 	key_check(key);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	ret = __key_link_begin(keyring, key->type, key->description, &prealloc);
Josh Boyer 9cb73b
+	ret = __key_link_begin(keyring, &key->index_key, &prealloc);
Josh Boyer 9cb73b
 	if (ret == 0) {
Josh Boyer 9cb73b
 		ret = __key_link_check_live_key(keyring, key);
Josh Boyer 9cb73b
 		if (ret == 0)
Josh Boyer 9cb73b
 			__key_link(keyring, key, &prealloc);
Josh Boyer 9cb73b
-		__key_link_end(keyring, key->type, prealloc);
Josh Boyer 9cb73b
+		__key_link_end(keyring, &key->index_key, prealloc);
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	return ret;
Josh Boyer 9cb73b
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
Josh Boyer 9cb73b
index 172115b..586cb79 100644
Josh Boyer 9cb73b
--- a/security/keys/request_key.c
Josh Boyer 9cb73b
+++ b/security/keys/request_key.c
Josh Boyer 9cb73b
@@ -352,6 +352,11 @@ static int construct_alloc_key(struct key_type *type,
Josh Boyer 9cb73b
 			       struct key_user *user,
Josh Boyer 9cb73b
 			       struct key **_key)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
+	const struct keyring_index_key index_key = {
Josh Boyer 9cb73b
+		.type		= type,
Josh Boyer 9cb73b
+		.description	= description,
Josh Boyer 9cb73b
+		.desc_len	= strlen(description),
Josh Boyer 9cb73b
+	};
Josh Boyer 9cb73b
 	const struct cred *cred = current_cred();
Josh Boyer 9cb73b
 	unsigned long prealloc;
Josh Boyer 9cb73b
 	struct key *key;
Josh Boyer 9cb73b
@@ -379,8 +384,7 @@ static int construct_alloc_key(struct key_type *type,
Josh Boyer 9cb73b
 	set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	if (dest_keyring) {
Josh Boyer 9cb73b
-		ret = __key_link_begin(dest_keyring, type, description,
Josh Boyer 9cb73b
-				       &prealloc);
Josh Boyer 9cb73b
+		ret = __key_link_begin(dest_keyring, &index_key, &prealloc);
Josh Boyer 9cb73b
 		if (ret < 0)
Josh Boyer 9cb73b
 			goto link_prealloc_failed;
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
@@ -400,7 +404,7 @@ static int construct_alloc_key(struct key_type *type,
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	mutex_unlock(&key_construction_mutex);
Josh Boyer 9cb73b
 	if (dest_keyring)
Josh Boyer 9cb73b
-		__key_link_end(dest_keyring, type, prealloc);
Josh Boyer 9cb73b
+		__key_link_end(dest_keyring, &index_key, prealloc);
Josh Boyer 9cb73b
 	mutex_unlock(&user->cons_lock);
Josh Boyer 9cb73b
 	*_key = key;
Josh Boyer 9cb73b
 	kleave(" = 0 [%d]", key_serial(key));
Josh Boyer 9cb73b
@@ -416,7 +420,7 @@ key_already_present:
Josh Boyer 9cb73b
 		ret = __key_link_check_live_key(dest_keyring, key);
Josh Boyer 9cb73b
 		if (ret == 0)
Josh Boyer 9cb73b
 			__key_link(dest_keyring, key, &prealloc);
Josh Boyer 9cb73b
-		__key_link_end(dest_keyring, type, prealloc);
Josh Boyer 9cb73b
+		__key_link_end(dest_keyring, &index_key, prealloc);
Josh Boyer 9cb73b
 		if (ret < 0)
Josh Boyer 9cb73b
 			goto link_check_failed;
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
-- 
Josh Boyer 9cb73b
1.8.3.1
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Josh Boyer 9cb73b
From eca8dad5cd291d2baf2d20372fcb0af9e75e25ea Mon Sep 17 00:00:00 2001
Josh Boyer 9cb73b
From: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
Date: Fri, 30 Aug 2013 15:37:52 +0100
Josh Boyer 9cb73b
Subject: [PATCH 05/10] KEYS: Introduce a search context structure
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Search functions pass around a bunch of arguments, each of which gets copied
Josh Boyer 9cb73b
with each call.  Introduce a search context structure to hold these.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Whilst we're at it, create a search flag that indicates whether the search
Josh Boyer 9cb73b
should be directly to the description or whether it should iterate through all
Josh Boyer 9cb73b
keys looking for a non-description match.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
This will be useful when keyrings use a generic data struct with generic
Josh Boyer 9cb73b
routines to manage their content as the search terms can just be passed
Josh Boyer 9cb73b
through to the iterator callback function.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Also, for future use, the data to be supplied to the match function is
Josh Boyer 9cb73b
separated from the description pointer in the search context.  This makes it
Josh Boyer 9cb73b
clear which is being supplied.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Signed-off-by: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
---
Josh Boyer 9cb73b
 include/linux/key-type.h         |   5 ++
Josh Boyer 9cb73b
 security/keys/internal.h         |  40 +++++++------
Josh Boyer 9cb73b
 security/keys/keyring.c          |  70 +++++++++++------------
Josh Boyer 9cb73b
 security/keys/proc.c             |  17 ++++--
Josh Boyer 9cb73b
 security/keys/process_keys.c     | 117 +++++++++++++++++++--------------------
Josh Boyer 9cb73b
 security/keys/request_key.c      |  56 +++++++++----------
Josh Boyer 9cb73b
 security/keys/request_key_auth.c |  14 +++--
Josh Boyer 9cb73b
 security/keys/user_defined.c     |  18 +++---
Josh Boyer 9cb73b
 8 files changed, 179 insertions(+), 158 deletions(-)
Josh Boyer 9cb73b
Josh Boyer 9cb73b
diff --git a/include/linux/key-type.h b/include/linux/key-type.h
Josh Boyer 9cb73b
index 518a53a..f58737b 100644
Josh Boyer 9cb73b
--- a/include/linux/key-type.h
Josh Boyer 9cb73b
+++ b/include/linux/key-type.h
Josh Boyer 9cb73b
@@ -63,6 +63,11 @@ struct key_type {
Josh Boyer 9cb73b
 	 */
Josh Boyer 9cb73b
 	size_t def_datalen;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
+	/* Default key search algorithm. */
Josh Boyer 9cb73b
+	unsigned def_lookup_type;
Josh Boyer 9cb73b
+#define KEYRING_SEARCH_LOOKUP_DIRECT	0x0000	/* Direct lookup by description. */
Josh Boyer 9cb73b
+#define KEYRING_SEARCH_LOOKUP_ITERATE	0x0001	/* Iterative search. */
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
 	/* vet a description */
Josh Boyer 9cb73b
 	int (*vet_description)(const char *description);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
diff --git a/security/keys/internal.h b/security/keys/internal.h
Josh Boyer 9cb73b
index 77441dd..f4bf938 100644
Josh Boyer 9cb73b
--- a/security/keys/internal.h
Josh Boyer 9cb73b
+++ b/security/keys/internal.h
Josh Boyer 9cb73b
@@ -107,23 +107,31 @@ extern struct key *keyring_search_instkey(struct key *keyring,
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 typedef int (*key_match_func_t)(const struct key *, const void *);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
+struct keyring_search_context {
Josh Boyer 9cb73b
+	struct keyring_index_key index_key;
Josh Boyer 9cb73b
+	const struct cred	*cred;
Josh Boyer 9cb73b
+	key_match_func_t	match;
Josh Boyer 9cb73b
+	const void		*match_data;
Josh Boyer 9cb73b
+	unsigned		flags;
Josh Boyer 9cb73b
+#define KEYRING_SEARCH_LOOKUP_TYPE	0x0001	/* [as type->def_lookup_type] */
Josh Boyer 9cb73b
+#define KEYRING_SEARCH_NO_STATE_CHECK	0x0002	/* Skip state checks */
Josh Boyer 9cb73b
+#define KEYRING_SEARCH_DO_STATE_CHECK	0x0004	/* Override NO_STATE_CHECK */
Josh Boyer 9cb73b
+#define KEYRING_SEARCH_NO_UPDATE_TIME	0x0008	/* Don't update times */
Josh Boyer 9cb73b
+#define KEYRING_SEARCH_NO_CHECK_PERM	0x0010	/* Don't check permissions */
Josh Boyer 9cb73b
+#define KEYRING_SEARCH_DETECT_TOO_DEEP	0x0020	/* Give an error on excessive depth */
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	/* Internal stuff */
Josh Boyer 9cb73b
+	int			skipped_ret;
Josh Boyer 9cb73b
+	bool			possessed;
Josh Boyer 9cb73b
+	key_ref_t		result;
Josh Boyer 9cb73b
+	struct timespec		now;
Josh Boyer 9cb73b
+};
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
 extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
Josh Boyer 9cb73b
-				    const struct cred *cred,
Josh Boyer 9cb73b
-				    struct key_type *type,
Josh Boyer 9cb73b
-				    const void *description,
Josh Boyer 9cb73b
-				    key_match_func_t match,
Josh Boyer 9cb73b
-				    bool no_state_check);
Josh Boyer 9cb73b
-
Josh Boyer 9cb73b
-extern key_ref_t search_my_process_keyrings(struct key_type *type,
Josh Boyer 9cb73b
-					    const void *description,
Josh Boyer 9cb73b
-					    key_match_func_t match,
Josh Boyer 9cb73b
-					    bool no_state_check,
Josh Boyer 9cb73b
-					    const struct cred *cred);
Josh Boyer 9cb73b
-extern key_ref_t search_process_keyrings(struct key_type *type,
Josh Boyer 9cb73b
-					 const void *description,
Josh Boyer 9cb73b
-					 key_match_func_t match,
Josh Boyer 9cb73b
-					 bool no_state_check,
Josh Boyer 9cb73b
-					 const struct cred *cred);
Josh Boyer 9cb73b
+				    struct keyring_search_context *ctx);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+extern key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx);
Josh Boyer 9cb73b
+extern key_ref_t search_process_keyrings(struct keyring_search_context *ctx);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
Josh Boyer 9cb73b
index c7f59f9..b42f2d4 100644
Josh Boyer 9cb73b
--- a/security/keys/keyring.c
Josh Boyer 9cb73b
+++ b/security/keys/keyring.c
Josh Boyer 9cb73b
@@ -280,11 +280,7 @@ EXPORT_SYMBOL(keyring_alloc);
Josh Boyer 9cb73b
 /**
Josh Boyer 9cb73b
  * keyring_search_aux - Search a keyring tree for a key matching some criteria
Josh Boyer 9cb73b
  * @keyring_ref: A pointer to the keyring with possession indicator.
Josh Boyer 9cb73b
- * @cred: The credentials to use for permissions checks.
Josh Boyer 9cb73b
- * @type: The type of key to search for.
Josh Boyer 9cb73b
- * @description: Parameter for @match.
Josh Boyer 9cb73b
- * @match: Function to rule on whether or not a key is the one required.
Josh Boyer 9cb73b
- * @no_state_check: Don't check if a matching key is bad
Josh Boyer 9cb73b
+ * @ctx: The keyring search context.
Josh Boyer 9cb73b
  *
Josh Boyer 9cb73b
  * Search the supplied keyring tree for a key that matches the criteria given.
Josh Boyer 9cb73b
  * The root keyring and any linked keyrings must grant Search permission to the
Josh Boyer 9cb73b
@@ -314,11 +310,7 @@ EXPORT_SYMBOL(keyring_alloc);
Josh Boyer 9cb73b
  * @keyring_ref is propagated to the returned key reference.
Josh Boyer 9cb73b
  */
Josh Boyer 9cb73b
 key_ref_t keyring_search_aux(key_ref_t keyring_ref,
Josh Boyer 9cb73b
-			     const struct cred *cred,
Josh Boyer 9cb73b
-			     struct key_type *type,
Josh Boyer 9cb73b
-			     const void *description,
Josh Boyer 9cb73b
-			     key_match_func_t match,
Josh Boyer 9cb73b
-			     bool no_state_check)
Josh Boyer 9cb73b
+			     struct keyring_search_context *ctx)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
 	struct {
Josh Boyer 9cb73b
 		/* Need a separate keylist pointer for RCU purposes */
Josh Boyer 9cb73b
@@ -328,20 +320,18 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
Josh Boyer 9cb73b
 	} stack[KEYRING_SEARCH_MAX_DEPTH];
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	struct keyring_list *keylist;
Josh Boyer 9cb73b
-	struct timespec now;
Josh Boyer 9cb73b
 	unsigned long kflags;
Josh Boyer 9cb73b
 	struct key *keyring, *key;
Josh Boyer 9cb73b
 	key_ref_t key_ref;
Josh Boyer 9cb73b
-	bool possessed;
Josh Boyer 9cb73b
 	long err;
Josh Boyer 9cb73b
 	int sp, nkeys, kix;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	keyring = key_ref_to_ptr(keyring_ref);
Josh Boyer 9cb73b
-	possessed = is_key_possessed(keyring_ref);
Josh Boyer 9cb73b
+	ctx->possessed = is_key_possessed(keyring_ref);
Josh Boyer 9cb73b
 	key_check(keyring);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	/* top keyring must have search permission to begin the search */
Josh Boyer 9cb73b
-	err = key_task_permission(keyring_ref, cred, KEY_SEARCH);
Josh Boyer 9cb73b
+	err = key_task_permission(keyring_ref, ctx->cred, KEY_SEARCH);
Josh Boyer 9cb73b
 	if (err < 0) {
Josh Boyer 9cb73b
 		key_ref = ERR_PTR(err);
Josh Boyer 9cb73b
 		goto error;
Josh Boyer 9cb73b
@@ -353,7 +343,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	rcu_read_lock();
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	now = current_kernel_time();
Josh Boyer 9cb73b
+	ctx->now = current_kernel_time();
Josh Boyer 9cb73b
 	err = -EAGAIN;
Josh Boyer 9cb73b
 	sp = 0;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -361,16 +351,17 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
Josh Boyer 9cb73b
 	 * are looking for */
Josh Boyer 9cb73b
 	key_ref = ERR_PTR(-EAGAIN);
Josh Boyer 9cb73b
 	kflags = keyring->flags;
Josh Boyer 9cb73b
-	if (keyring->type == type && match(keyring, description)) {
Josh Boyer 9cb73b
+	if (keyring->type == ctx->index_key.type &&
Josh Boyer 9cb73b
+	    ctx->match(keyring, ctx->match_data)) {
Josh Boyer 9cb73b
 		key = keyring;
Josh Boyer 9cb73b
-		if (no_state_check)
Josh Boyer 9cb73b
+		if (ctx->flags & KEYRING_SEARCH_NO_STATE_CHECK)
Josh Boyer 9cb73b
 			goto found;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 		/* check it isn't negative and hasn't expired or been
Josh Boyer 9cb73b
 		 * revoked */
Josh Boyer 9cb73b
 		if (kflags & (1 << KEY_FLAG_REVOKED))
Josh Boyer 9cb73b
 			goto error_2;
Josh Boyer 9cb73b
-		if (key->expiry && now.tv_sec >= key->expiry)
Josh Boyer 9cb73b
+		if (key->expiry && ctx->now.tv_sec >= key->expiry)
Josh Boyer 9cb73b
 			goto error_2;
Josh Boyer 9cb73b
 		key_ref = ERR_PTR(key->type_data.reject_error);
Josh Boyer 9cb73b
 		if (kflags & (1 << KEY_FLAG_NEGATIVE))
Josh Boyer 9cb73b
@@ -384,7 +375,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
Josh Boyer 9cb73b
 	if (kflags & ((1 << KEY_FLAG_INVALIDATED) |
Josh Boyer 9cb73b
 		      (1 << KEY_FLAG_REVOKED) |
Josh Boyer 9cb73b
 		      (1 << KEY_FLAG_NEGATIVE)) ||
Josh Boyer 9cb73b
-	    (keyring->expiry && now.tv_sec >= keyring->expiry))
Josh Boyer 9cb73b
+	    (keyring->expiry && ctx->now.tv_sec >= keyring->expiry))
Josh Boyer 9cb73b
 		goto error_2;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	/* start processing a new keyring */
Josh Boyer 9cb73b
@@ -406,29 +397,29 @@ descend:
Josh Boyer 9cb73b
 		kflags = key->flags;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 		/* ignore keys not of this type */
Josh Boyer 9cb73b
-		if (key->type != type)
Josh Boyer 9cb73b
+		if (key->type != ctx->index_key.type)
Josh Boyer 9cb73b
 			continue;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 		/* skip invalidated, revoked and expired keys */
Josh Boyer 9cb73b
-		if (!no_state_check) {
Josh Boyer 9cb73b
+		if (!(ctx->flags & KEYRING_SEARCH_NO_STATE_CHECK)) {
Josh Boyer 9cb73b
 			if (kflags & ((1 << KEY_FLAG_INVALIDATED) |
Josh Boyer 9cb73b
 				      (1 << KEY_FLAG_REVOKED)))
Josh Boyer 9cb73b
 				continue;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-			if (key->expiry && now.tv_sec >= key->expiry)
Josh Boyer 9cb73b
+			if (key->expiry && ctx->now.tv_sec >= key->expiry)
Josh Boyer 9cb73b
 				continue;
Josh Boyer 9cb73b
 		}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 		/* keys that don't match */
Josh Boyer 9cb73b
-		if (!match(key, description))
Josh Boyer 9cb73b
+		if (!ctx->match(key, ctx->match_data))
Josh Boyer 9cb73b
 			continue;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 		/* key must have search permissions */
Josh Boyer 9cb73b
-		if (key_task_permission(make_key_ref(key, possessed),
Josh Boyer 9cb73b
-					cred, KEY_SEARCH) < 0)
Josh Boyer 9cb73b
+		if (key_task_permission(make_key_ref(key, ctx->possessed),
Josh Boyer 9cb73b
+					ctx->cred, KEY_SEARCH) < 0)
Josh Boyer 9cb73b
 			continue;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-		if (no_state_check)
Josh Boyer 9cb73b
+		if (ctx->flags & KEYRING_SEARCH_NO_STATE_CHECK)
Josh Boyer 9cb73b
 			goto found;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 		/* we set a different error code if we pass a negative key */
Josh Boyer 9cb73b
@@ -456,8 +447,8 @@ ascend:
Josh Boyer 9cb73b
 		if (sp >= KEYRING_SEARCH_MAX_DEPTH)
Josh Boyer 9cb73b
 			continue;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-		if (key_task_permission(make_key_ref(key, possessed),
Josh Boyer 9cb73b
-					cred, KEY_SEARCH) < 0)
Josh Boyer 9cb73b
+		if (key_task_permission(make_key_ref(key, ctx->possessed),
Josh Boyer 9cb73b
+					ctx->cred, KEY_SEARCH) < 0)
Josh Boyer 9cb73b
 			continue;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 		/* stack the current position */
Josh Boyer 9cb73b
@@ -489,12 +480,12 @@ not_this_keyring:
Josh Boyer 9cb73b
 	/* we found a viable match */
Josh Boyer 9cb73b
 found:
Josh Boyer 9cb73b
 	atomic_inc(&key->usage);
Josh Boyer 9cb73b
-	key->last_used_at = now.tv_sec;
Josh Boyer 9cb73b
-	keyring->last_used_at = now.tv_sec;
Josh Boyer 9cb73b
+	key->last_used_at = ctx->now.tv_sec;
Josh Boyer 9cb73b
+	keyring->last_used_at = ctx->now.tv_sec;
Josh Boyer 9cb73b
 	while (sp > 0)
Josh Boyer 9cb73b
-		stack[--sp].keyring->last_used_at = now.tv_sec;
Josh Boyer 9cb73b
+		stack[--sp].keyring->last_used_at = ctx->now.tv_sec;
Josh Boyer 9cb73b
 	key_check(key);
Josh Boyer 9cb73b
-	key_ref = make_key_ref(key, possessed);
Josh Boyer 9cb73b
+	key_ref = make_key_ref(key, ctx->possessed);
Josh Boyer 9cb73b
 error_2:
Josh Boyer 9cb73b
 	rcu_read_unlock();
Josh Boyer 9cb73b
 error:
Josh Boyer 9cb73b
@@ -514,11 +505,20 @@ key_ref_t keyring_search(key_ref_t keyring,
Josh Boyer 9cb73b
 			 struct key_type *type,
Josh Boyer 9cb73b
 			 const char *description)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
-	if (!type->match)
Josh Boyer 9cb73b
+	struct keyring_search_context ctx = {
Josh Boyer 9cb73b
+		.index_key.type		= type,
Josh Boyer 9cb73b
+		.index_key.description	= description,
Josh Boyer 9cb73b
+		.cred			= current_cred(),
Josh Boyer 9cb73b
+		.match			= type->match,
Josh Boyer 9cb73b
+		.match_data		= description,
Josh Boyer 9cb73b
+		.flags			= (type->def_lookup_type |
Josh Boyer 9cb73b
+					   KEYRING_SEARCH_DO_STATE_CHECK),
Josh Boyer 9cb73b
+	};
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	if (!ctx.match)
Josh Boyer 9cb73b
 		return ERR_PTR(-ENOKEY);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	return keyring_search_aux(keyring, current->cred,
Josh Boyer 9cb73b
-				  type, description, type->match, false);
Josh Boyer 9cb73b
+	return keyring_search_aux(keyring, &ctx;;
Josh Boyer 9cb73b
 }
Josh Boyer 9cb73b
 EXPORT_SYMBOL(keyring_search);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
diff --git a/security/keys/proc.c b/security/keys/proc.c
Josh Boyer 9cb73b
index 217b685..88e9a46 100644
Josh Boyer 9cb73b
--- a/security/keys/proc.c
Josh Boyer 9cb73b
+++ b/security/keys/proc.c
Josh Boyer 9cb73b
@@ -182,7 +182,6 @@ static void proc_keys_stop(struct seq_file *p, void *v)
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 static int proc_keys_show(struct seq_file *m, void *v)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
-	const struct cred *cred = current_cred();
Josh Boyer 9cb73b
 	struct rb_node *_p = v;
Josh Boyer 9cb73b
 	struct key *key = rb_entry(_p, struct key, serial_node);
Josh Boyer 9cb73b
 	struct timespec now;
Josh Boyer 9cb73b
@@ -191,15 +190,23 @@ static int proc_keys_show(struct seq_file *m, void *v)
Josh Boyer 9cb73b
 	char xbuf[12];
Josh Boyer 9cb73b
 	int rc;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
+	struct keyring_search_context ctx = {
Josh Boyer 9cb73b
+		.index_key.type		= key->type,
Josh Boyer 9cb73b
+		.index_key.description	= key->description,
Josh Boyer 9cb73b
+		.cred			= current_cred(),
Josh Boyer 9cb73b
+		.match			= lookup_user_key_possessed,
Josh Boyer 9cb73b
+		.match_data		= key,
Josh Boyer 9cb73b
+		.flags			= (KEYRING_SEARCH_NO_STATE_CHECK |
Josh Boyer 9cb73b
+					   KEYRING_SEARCH_LOOKUP_DIRECT),
Josh Boyer 9cb73b
+	};
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
 	key_ref = make_key_ref(key, 0);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	/* determine if the key is possessed by this process (a test we can
Josh Boyer 9cb73b
 	 * skip if the key does not indicate the possessor can view it
Josh Boyer 9cb73b
 	 */
Josh Boyer 9cb73b
 	if (key->perm & KEY_POS_VIEW) {
Josh Boyer 9cb73b
-		skey_ref = search_my_process_keyrings(key->type, key,
Josh Boyer 9cb73b
-						      lookup_user_key_possessed,
Josh Boyer 9cb73b
-						      true, cred);
Josh Boyer 9cb73b
+		skey_ref = search_my_process_keyrings(&ctx;;
Josh Boyer 9cb73b
 		if (!IS_ERR(skey_ref)) {
Josh Boyer 9cb73b
 			key_ref_put(skey_ref);
Josh Boyer 9cb73b
 			key_ref = make_key_ref(key, 1);
Josh Boyer 9cb73b
@@ -211,7 +218,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
Josh Boyer 9cb73b
 	 * - the caller holds a spinlock, and thus the RCU read lock, making our
Josh Boyer 9cb73b
 	 *   access to __current_cred() safe
Josh Boyer 9cb73b
 	 */
Josh Boyer 9cb73b
-	rc = key_task_permission(key_ref, cred, KEY_VIEW);
Josh Boyer 9cb73b
+	rc = key_task_permission(key_ref, ctx.cred, KEY_VIEW);
Josh Boyer 9cb73b
 	if (rc < 0)
Josh Boyer 9cb73b
 		return 0;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
Josh Boyer 9cb73b
index a3410d6..e68a3e0 100644
Josh Boyer 9cb73b
--- a/security/keys/process_keys.c
Josh Boyer 9cb73b
+++ b/security/keys/process_keys.c
Josh Boyer 9cb73b
@@ -319,11 +319,7 @@ void key_fsgid_changed(struct task_struct *tsk)
Josh Boyer 9cb73b
  * In the case of a successful return, the possession attribute is set on the
Josh Boyer 9cb73b
  * returned key reference.
Josh Boyer 9cb73b
  */
Josh Boyer 9cb73b
-key_ref_t search_my_process_keyrings(struct key_type *type,
Josh Boyer 9cb73b
-				     const void *description,
Josh Boyer 9cb73b
-				     key_match_func_t match,
Josh Boyer 9cb73b
-				     bool no_state_check,
Josh Boyer 9cb73b
-				     const struct cred *cred)
Josh Boyer 9cb73b
+key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
 	key_ref_t key_ref, ret, err;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -339,10 +335,9 @@ key_ref_t search_my_process_keyrings(struct key_type *type,
Josh Boyer 9cb73b
 	err = ERR_PTR(-EAGAIN);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	/* search the thread keyring first */
Josh Boyer 9cb73b
-	if (cred->thread_keyring) {
Josh Boyer 9cb73b
+	if (ctx->cred->thread_keyring) {
Josh Boyer 9cb73b
 		key_ref = keyring_search_aux(
Josh Boyer 9cb73b
-			make_key_ref(cred->thread_keyring, 1),
Josh Boyer 9cb73b
-			cred, type, description, match, no_state_check);
Josh Boyer 9cb73b
+			make_key_ref(ctx->cred->thread_keyring, 1), ctx);
Josh Boyer 9cb73b
 		if (!IS_ERR(key_ref))
Josh Boyer 9cb73b
 			goto found;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -358,10 +353,9 @@ key_ref_t search_my_process_keyrings(struct key_type *type,
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	/* search the process keyring second */
Josh Boyer 9cb73b
-	if (cred->process_keyring) {
Josh Boyer 9cb73b
+	if (ctx->cred->process_keyring) {
Josh Boyer 9cb73b
 		key_ref = keyring_search_aux(
Josh Boyer 9cb73b
-			make_key_ref(cred->process_keyring, 1),
Josh Boyer 9cb73b
-			cred, type, description, match, no_state_check);
Josh Boyer 9cb73b
+			make_key_ref(ctx->cred->process_keyring, 1), ctx);
Josh Boyer 9cb73b
 		if (!IS_ERR(key_ref))
Josh Boyer 9cb73b
 			goto found;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -379,11 +373,11 @@ key_ref_t search_my_process_keyrings(struct key_type *type,
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	/* search the session keyring */
Josh Boyer 9cb73b
-	if (cred->session_keyring) {
Josh Boyer 9cb73b
+	if (ctx->cred->session_keyring) {
Josh Boyer 9cb73b
 		rcu_read_lock();
Josh Boyer 9cb73b
 		key_ref = keyring_search_aux(
Josh Boyer 9cb73b
-			make_key_ref(rcu_dereference(cred->session_keyring), 1),
Josh Boyer 9cb73b
-			cred, type, description, match, no_state_check);
Josh Boyer 9cb73b
+			make_key_ref(rcu_dereference(ctx->cred->session_keyring), 1),
Josh Boyer 9cb73b
+			ctx);
Josh Boyer 9cb73b
 		rcu_read_unlock();
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 		if (!IS_ERR(key_ref))
Josh Boyer 9cb73b
@@ -402,10 +396,10 @@ key_ref_t search_my_process_keyrings(struct key_type *type,
Josh Boyer 9cb73b
 		}
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
 	/* or search the user-session keyring */
Josh Boyer 9cb73b
-	else if (cred->user->session_keyring) {
Josh Boyer 9cb73b
+	else if (ctx->cred->user->session_keyring) {
Josh Boyer 9cb73b
 		key_ref = keyring_search_aux(
Josh Boyer 9cb73b
-			make_key_ref(cred->user->session_keyring, 1),
Josh Boyer 9cb73b
-			cred, type, description, match, no_state_check);
Josh Boyer 9cb73b
+			make_key_ref(ctx->cred->user->session_keyring, 1),
Josh Boyer 9cb73b
+			ctx);
Josh Boyer 9cb73b
 		if (!IS_ERR(key_ref))
Josh Boyer 9cb73b
 			goto found;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -437,19 +431,14 @@ found:
Josh Boyer 9cb73b
  *
Josh Boyer 9cb73b
  * Return same as search_my_process_keyrings().
Josh Boyer 9cb73b
  */
Josh Boyer 9cb73b
-key_ref_t search_process_keyrings(struct key_type *type,
Josh Boyer 9cb73b
-				  const void *description,
Josh Boyer 9cb73b
-				  key_match_func_t match,
Josh Boyer 9cb73b
-				  bool no_state_check,
Josh Boyer 9cb73b
-				  const struct cred *cred)
Josh Boyer 9cb73b
+key_ref_t search_process_keyrings(struct keyring_search_context *ctx)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
 	struct request_key_auth *rka;
Josh Boyer 9cb73b
 	key_ref_t key_ref, ret = ERR_PTR(-EACCES), err;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	might_sleep();
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	key_ref = search_my_process_keyrings(type, description, match,
Josh Boyer 9cb73b
-					     no_state_check, cred);
Josh Boyer 9cb73b
+	key_ref = search_my_process_keyrings(ctx);
Josh Boyer 9cb73b
 	if (!IS_ERR(key_ref))
Josh Boyer 9cb73b
 		goto found;
Josh Boyer 9cb73b
 	err = key_ref;
Josh Boyer 9cb73b
@@ -458,19 +447,21 @@ key_ref_t search_process_keyrings(struct key_type *type,
Josh Boyer 9cb73b
 	 * search the keyrings of the process mentioned there
Josh Boyer 9cb73b
 	 * - we don't permit access to request_key auth keys via this method
Josh Boyer 9cb73b
 	 */
Josh Boyer 9cb73b
-	if (cred->request_key_auth &&
Josh Boyer 9cb73b
-	    cred == current_cred() &&
Josh Boyer 9cb73b
-	    type != &key_type_request_key_auth
Josh Boyer 9cb73b
+	if (ctx->cred->request_key_auth &&
Josh Boyer 9cb73b
+	    ctx->cred == current_cred() &&
Josh Boyer 9cb73b
+	    ctx->index_key.type != &key_type_request_key_auth
Josh Boyer 9cb73b
 	    ) {
Josh Boyer 9cb73b
+		const struct cred *cred = ctx->cred;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
 		/* defend against the auth key being revoked */
Josh Boyer 9cb73b
 		down_read(&cred->request_key_auth->sem);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-		if (key_validate(cred->request_key_auth) == 0) {
Josh Boyer 9cb73b
-			rka = cred->request_key_auth->payload.data;
Josh Boyer 9cb73b
+		if (key_validate(ctx->cred->request_key_auth) == 0) {
Josh Boyer 9cb73b
+			rka = ctx->cred->request_key_auth->payload.data;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-			key_ref = search_process_keyrings(type, description,
Josh Boyer 9cb73b
-							  match, no_state_check,
Josh Boyer 9cb73b
-							  rka->cred);
Josh Boyer 9cb73b
+			ctx->cred = rka->cred;
Josh Boyer 9cb73b
+			key_ref = search_process_keyrings(ctx);
Josh Boyer 9cb73b
+			ctx->cred = cred;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 			up_read(&cred->request_key_auth->sem);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -524,19 +515,23 @@ int lookup_user_key_possessed(const struct key *key, const void *target)
Josh Boyer 9cb73b
 key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
Josh Boyer 9cb73b
 			  key_perm_t perm)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
+	struct keyring_search_context ctx = {
Josh Boyer 9cb73b
+		.match	= lookup_user_key_possessed,
Josh Boyer 9cb73b
+		.flags	= (KEYRING_SEARCH_NO_STATE_CHECK |
Josh Boyer 9cb73b
+			   KEYRING_SEARCH_LOOKUP_DIRECT),
Josh Boyer 9cb73b
+	};
Josh Boyer 9cb73b
 	struct request_key_auth *rka;
Josh Boyer 9cb73b
-	const struct cred *cred;
Josh Boyer 9cb73b
 	struct key *key;
Josh Boyer 9cb73b
 	key_ref_t key_ref, skey_ref;
Josh Boyer 9cb73b
 	int ret;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 try_again:
Josh Boyer 9cb73b
-	cred = get_current_cred();
Josh Boyer 9cb73b
+	ctx.cred = get_current_cred();
Josh Boyer 9cb73b
 	key_ref = ERR_PTR(-ENOKEY);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	switch (id) {
Josh Boyer 9cb73b
 	case KEY_SPEC_THREAD_KEYRING:
Josh Boyer 9cb73b
-		if (!cred->thread_keyring) {
Josh Boyer 9cb73b
+		if (!ctx.cred->thread_keyring) {
Josh Boyer 9cb73b
 			if (!(lflags & KEY_LOOKUP_CREATE))
Josh Boyer 9cb73b
 				goto error;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -548,13 +543,13 @@ try_again:
Josh Boyer 9cb73b
 			goto reget_creds;
Josh Boyer 9cb73b
 		}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-		key = cred->thread_keyring;
Josh Boyer 9cb73b
+		key = ctx.cred->thread_keyring;
Josh Boyer 9cb73b
 		atomic_inc(&key->usage);
Josh Boyer 9cb73b
 		key_ref = make_key_ref(key, 1);
Josh Boyer 9cb73b
 		break;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	case KEY_SPEC_PROCESS_KEYRING:
Josh Boyer 9cb73b
-		if (!cred->process_keyring) {
Josh Boyer 9cb73b
+		if (!ctx.cred->process_keyring) {
Josh Boyer 9cb73b
 			if (!(lflags & KEY_LOOKUP_CREATE))
Josh Boyer 9cb73b
 				goto error;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -566,13 +561,13 @@ try_again:
Josh Boyer 9cb73b
 			goto reget_creds;
Josh Boyer 9cb73b
 		}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-		key = cred->process_keyring;
Josh Boyer 9cb73b
+		key = ctx.cred->process_keyring;
Josh Boyer 9cb73b
 		atomic_inc(&key->usage);
Josh Boyer 9cb73b
 		key_ref = make_key_ref(key, 1);
Josh Boyer 9cb73b
 		break;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	case KEY_SPEC_SESSION_KEYRING:
Josh Boyer 9cb73b
-		if (!cred->session_keyring) {
Josh Boyer 9cb73b
+		if (!ctx.cred->session_keyring) {
Josh Boyer 9cb73b
 			/* always install a session keyring upon access if one
Josh Boyer 9cb73b
 			 * doesn't exist yet */
Josh Boyer 9cb73b
 			ret = install_user_keyrings();
Josh Boyer 9cb73b
@@ -582,13 +577,13 @@ try_again:
Josh Boyer 9cb73b
 				ret = join_session_keyring(NULL);
Josh Boyer 9cb73b
 			else
Josh Boyer 9cb73b
 				ret = install_session_keyring(
Josh Boyer 9cb73b
-					cred->user->session_keyring);
Josh Boyer 9cb73b
+					ctx.cred->user->session_keyring);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 			if (ret < 0)
Josh Boyer 9cb73b
 				goto error;
Josh Boyer 9cb73b
 			goto reget_creds;
Josh Boyer 9cb73b
-		} else if (cred->session_keyring ==
Josh Boyer 9cb73b
-			   cred->user->session_keyring &&
Josh Boyer 9cb73b
+		} else if (ctx.cred->session_keyring ==
Josh Boyer 9cb73b
+			   ctx.cred->user->session_keyring &&
Josh Boyer 9cb73b
 			   lflags & KEY_LOOKUP_CREATE) {
Josh Boyer 9cb73b
 			ret = join_session_keyring(NULL);
Josh Boyer 9cb73b
 			if (ret < 0)
Josh Boyer 9cb73b
@@ -597,32 +592,32 @@ try_again:
Josh Boyer 9cb73b
 		}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 		rcu_read_lock();
Josh Boyer 9cb73b
-		key = rcu_dereference(cred->session_keyring);
Josh Boyer 9cb73b
+		key = rcu_dereference(ctx.cred->session_keyring);
Josh Boyer 9cb73b
 		atomic_inc(&key->usage);
Josh Boyer 9cb73b
 		rcu_read_unlock();
Josh Boyer 9cb73b
 		key_ref = make_key_ref(key, 1);
Josh Boyer 9cb73b
 		break;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	case KEY_SPEC_USER_KEYRING:
Josh Boyer 9cb73b
-		if (!cred->user->uid_keyring) {
Josh Boyer 9cb73b
+		if (!ctx.cred->user->uid_keyring) {
Josh Boyer 9cb73b
 			ret = install_user_keyrings();
Josh Boyer 9cb73b
 			if (ret < 0)
Josh Boyer 9cb73b
 				goto error;
Josh Boyer 9cb73b
 		}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-		key = cred->user->uid_keyring;
Josh Boyer 9cb73b
+		key = ctx.cred->user->uid_keyring;
Josh Boyer 9cb73b
 		atomic_inc(&key->usage);
Josh Boyer 9cb73b
 		key_ref = make_key_ref(key, 1);
Josh Boyer 9cb73b
 		break;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	case KEY_SPEC_USER_SESSION_KEYRING:
Josh Boyer 9cb73b
-		if (!cred->user->session_keyring) {
Josh Boyer 9cb73b
+		if (!ctx.cred->user->session_keyring) {
Josh Boyer 9cb73b
 			ret = install_user_keyrings();
Josh Boyer 9cb73b
 			if (ret < 0)
Josh Boyer 9cb73b
 				goto error;
Josh Boyer 9cb73b
 		}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-		key = cred->user->session_keyring;
Josh Boyer 9cb73b
+		key = ctx.cred->user->session_keyring;
Josh Boyer 9cb73b
 		atomic_inc(&key->usage);
Josh Boyer 9cb73b
 		key_ref = make_key_ref(key, 1);
Josh Boyer 9cb73b
 		break;
Josh Boyer 9cb73b
@@ -633,7 +628,7 @@ try_again:
Josh Boyer 9cb73b
 		goto error;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	case KEY_SPEC_REQKEY_AUTH_KEY:
Josh Boyer 9cb73b
-		key = cred->request_key_auth;
Josh Boyer 9cb73b
+		key = ctx.cred->request_key_auth;
Josh Boyer 9cb73b
 		if (!key)
Josh Boyer 9cb73b
 			goto error;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -642,20 +637,20 @@ try_again:
Josh Boyer 9cb73b
 		break;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	case KEY_SPEC_REQUESTOR_KEYRING:
Josh Boyer 9cb73b
-		if (!cred->request_key_auth)
Josh Boyer 9cb73b
+		if (!ctx.cred->request_key_auth)
Josh Boyer 9cb73b
 			goto error;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-		down_read(&cred->request_key_auth->sem);
Josh Boyer 9cb73b
+		down_read(&ctx.cred->request_key_auth->sem);
Josh Boyer 9cb73b
 		if (test_bit(KEY_FLAG_REVOKED,
Josh Boyer 9cb73b
-			     &cred->request_key_auth->flags)) {
Josh Boyer 9cb73b
+			     &ctx.cred->request_key_auth->flags)) {
Josh Boyer 9cb73b
 			key_ref = ERR_PTR(-EKEYREVOKED);
Josh Boyer 9cb73b
 			key = NULL;
Josh Boyer 9cb73b
 		} else {
Josh Boyer 9cb73b
-			rka = cred->request_key_auth->payload.data;
Josh Boyer 9cb73b
+			rka = ctx.cred->request_key_auth->payload.data;
Josh Boyer 9cb73b
 			key = rka->dest_keyring;
Josh Boyer 9cb73b
 			atomic_inc(&key->usage);
Josh Boyer 9cb73b
 		}
Josh Boyer 9cb73b
-		up_read(&cred->request_key_auth->sem);
Josh Boyer 9cb73b
+		up_read(&ctx.cred->request_key_auth->sem);
Josh Boyer 9cb73b
 		if (!key)
Josh Boyer 9cb73b
 			goto error;
Josh Boyer 9cb73b
 		key_ref = make_key_ref(key, 1);
Josh Boyer 9cb73b
@@ -675,9 +670,13 @@ try_again:
Josh Boyer 9cb73b
 		key_ref = make_key_ref(key, 0);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 		/* check to see if we possess the key */
Josh Boyer 9cb73b
-		skey_ref = search_process_keyrings(key->type, key,
Josh Boyer 9cb73b
-						   lookup_user_key_possessed,
Josh Boyer 9cb73b
-						   true, cred);
Josh Boyer 9cb73b
+		ctx.index_key.type		= key->type;
Josh Boyer 9cb73b
+		ctx.index_key.description	= key->description;
Josh Boyer 9cb73b
+		ctx.index_key.desc_len		= strlen(key->description);
Josh Boyer 9cb73b
+		ctx.match_data			= key;
Josh Boyer 9cb73b
+		kdebug("check possessed");
Josh Boyer 9cb73b
+		skey_ref = search_process_keyrings(&ctx;;
Josh Boyer 9cb73b
+		kdebug("possessed=%p", skey_ref);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 		if (!IS_ERR(skey_ref)) {
Josh Boyer 9cb73b
 			key_put(key);
Josh Boyer 9cb73b
@@ -717,14 +716,14 @@ try_again:
Josh Boyer 9cb73b
 		goto invalid_key;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	/* check the permissions */
Josh Boyer 9cb73b
-	ret = key_task_permission(key_ref, cred, perm);
Josh Boyer 9cb73b
+	ret = key_task_permission(key_ref, ctx.cred, perm);
Josh Boyer 9cb73b
 	if (ret < 0)
Josh Boyer 9cb73b
 		goto invalid_key;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	key->last_used_at = current_kernel_time().tv_sec;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 error:
Josh Boyer 9cb73b
-	put_cred(cred);
Josh Boyer 9cb73b
+	put_cred(ctx.cred);
Josh Boyer 9cb73b
 	return key_ref;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 invalid_key:
Josh Boyer 9cb73b
@@ -735,7 +734,7 @@ invalid_key:
Josh Boyer 9cb73b
 	/* if we attempted to install a keyring, then it may have caused new
Josh Boyer 9cb73b
 	 * creds to be installed */
Josh Boyer 9cb73b
 reget_creds:
Josh Boyer 9cb73b
-	put_cred(cred);
Josh Boyer 9cb73b
+	put_cred(ctx.cred);
Josh Boyer 9cb73b
 	goto try_again;
Josh Boyer 9cb73b
 }
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
Josh Boyer 9cb73b
index 586cb79..ab75df4 100644
Josh Boyer 9cb73b
--- a/security/keys/request_key.c
Josh Boyer 9cb73b
+++ b/security/keys/request_key.c
Josh Boyer 9cb73b
@@ -345,38 +345,34 @@ static void construct_get_dest_keyring(struct key **_dest_keyring)
Josh Boyer 9cb73b
  * May return a key that's already under construction instead if there was a
Josh Boyer 9cb73b
  * race between two thread calling request_key().
Josh Boyer 9cb73b
  */
Josh Boyer 9cb73b
-static int construct_alloc_key(struct key_type *type,
Josh Boyer 9cb73b
-			       const char *description,
Josh Boyer 9cb73b
+static int construct_alloc_key(struct keyring_search_context *ctx,
Josh Boyer 9cb73b
 			       struct key *dest_keyring,
Josh Boyer 9cb73b
 			       unsigned long flags,
Josh Boyer 9cb73b
 			       struct key_user *user,
Josh Boyer 9cb73b
 			       struct key **_key)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
-	const struct keyring_index_key index_key = {
Josh Boyer 9cb73b
-		.type		= type,
Josh Boyer 9cb73b
-		.description	= description,
Josh Boyer 9cb73b
-		.desc_len	= strlen(description),
Josh Boyer 9cb73b
-	};
Josh Boyer 9cb73b
-	const struct cred *cred = current_cred();
Josh Boyer 9cb73b
 	unsigned long prealloc;
Josh Boyer 9cb73b
 	struct key *key;
Josh Boyer 9cb73b
 	key_perm_t perm;
Josh Boyer 9cb73b
 	key_ref_t key_ref;
Josh Boyer 9cb73b
 	int ret;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	kenter("%s,%s,,,", type->name, description);
Josh Boyer 9cb73b
+	kenter("%s,%s,,,",
Josh Boyer 9cb73b
+	       ctx->index_key.type->name, ctx->index_key.description);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	*_key = NULL;
Josh Boyer 9cb73b
 	mutex_lock(&user->cons_lock);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;
Josh Boyer 9cb73b
 	perm |= KEY_USR_VIEW;
Josh Boyer 9cb73b
-	if (type->read)
Josh Boyer 9cb73b
+	if (ctx->index_key.type->read)
Josh Boyer 9cb73b
 		perm |= KEY_POS_READ;
Josh Boyer 9cb73b
-	if (type == &key_type_keyring || type->update)
Josh Boyer 9cb73b
+	if (ctx->index_key.type == &key_type_keyring ||
Josh Boyer 9cb73b
+	    ctx->index_key.type->update)
Josh Boyer 9cb73b
 		perm |= KEY_POS_WRITE;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	key = key_alloc(type, description, cred->fsuid, cred->fsgid, cred,
Josh Boyer 9cb73b
+	key = key_alloc(ctx->index_key.type, ctx->index_key.description,
Josh Boyer 9cb73b
+			ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred,
Josh Boyer 9cb73b
 			perm, flags);
Josh Boyer 9cb73b
 	if (IS_ERR(key))
Josh Boyer 9cb73b
 		goto alloc_failed;
Josh Boyer 9cb73b
@@ -384,7 +380,7 @@ static int construct_alloc_key(struct key_type *type,
Josh Boyer 9cb73b
 	set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	if (dest_keyring) {
Josh Boyer 9cb73b
-		ret = __key_link_begin(dest_keyring, &index_key, &prealloc);
Josh Boyer 9cb73b
+		ret = __key_link_begin(dest_keyring, &ctx->index_key, &prealloc);
Josh Boyer 9cb73b
 		if (ret < 0)
Josh Boyer 9cb73b
 			goto link_prealloc_failed;
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
@@ -394,8 +390,7 @@ static int construct_alloc_key(struct key_type *type,
Josh Boyer 9cb73b
 	 * waited for locks */
Josh Boyer 9cb73b
 	mutex_lock(&key_construction_mutex);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	key_ref = search_process_keyrings(type, description, type->match,
Josh Boyer 9cb73b
-					  false, cred);
Josh Boyer 9cb73b
+	key_ref = search_process_keyrings(ctx);
Josh Boyer 9cb73b
 	if (!IS_ERR(key_ref))
Josh Boyer 9cb73b
 		goto key_already_present;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -404,7 +399,7 @@ static int construct_alloc_key(struct key_type *type,
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	mutex_unlock(&key_construction_mutex);
Josh Boyer 9cb73b
 	if (dest_keyring)
Josh Boyer 9cb73b
-		__key_link_end(dest_keyring, &index_key, prealloc);
Josh Boyer 9cb73b
+		__key_link_end(dest_keyring, &ctx->index_key, prealloc);
Josh Boyer 9cb73b
 	mutex_unlock(&user->cons_lock);
Josh Boyer 9cb73b
 	*_key = key;
Josh Boyer 9cb73b
 	kleave(" = 0 [%d]", key_serial(key));
Josh Boyer 9cb73b
@@ -420,7 +415,7 @@ key_already_present:
Josh Boyer 9cb73b
 		ret = __key_link_check_live_key(dest_keyring, key);
Josh Boyer 9cb73b
 		if (ret == 0)
Josh Boyer 9cb73b
 			__key_link(dest_keyring, key, &prealloc);
Josh Boyer 9cb73b
-		__key_link_end(dest_keyring, &index_key, prealloc);
Josh Boyer 9cb73b
+		__key_link_end(dest_keyring, &ctx->index_key, prealloc);
Josh Boyer 9cb73b
 		if (ret < 0)
Josh Boyer 9cb73b
 			goto link_check_failed;
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
@@ -449,8 +444,7 @@ alloc_failed:
Josh Boyer 9cb73b
 /*
Josh Boyer 9cb73b
  * Commence key construction.
Josh Boyer 9cb73b
  */
Josh Boyer 9cb73b
-static struct key *construct_key_and_link(struct key_type *type,
Josh Boyer 9cb73b
-					  const char *description,
Josh Boyer 9cb73b
+static struct key *construct_key_and_link(struct keyring_search_context *ctx,
Josh Boyer 9cb73b
 					  const char *callout_info,
Josh Boyer 9cb73b
 					  size_t callout_len,
Josh Boyer 9cb73b
 					  void *aux,
Josh Boyer 9cb73b
@@ -469,8 +463,7 @@ static struct key *construct_key_and_link(struct key_type *type,
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	construct_get_dest_keyring(&dest_keyring);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	ret = construct_alloc_key(type, description, dest_keyring, flags, user,
Josh Boyer 9cb73b
-				  &key);
Josh Boyer 9cb73b
+	ret = construct_alloc_key(ctx, dest_keyring, flags, user, &key);
Josh Boyer 9cb73b
 	key_user_put(user);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	if (ret == 0) {
Josh Boyer 9cb73b
@@ -534,18 +527,24 @@ struct key *request_key_and_link(struct key_type *type,
Josh Boyer 9cb73b
 				 struct key *dest_keyring,
Josh Boyer 9cb73b
 				 unsigned long flags)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
-	const struct cred *cred = current_cred();
Josh Boyer 9cb73b
+	struct keyring_search_context ctx = {
Josh Boyer 9cb73b
+		.index_key.type		= type,
Josh Boyer 9cb73b
+		.index_key.description	= description,
Josh Boyer 9cb73b
+		.cred			= current_cred(),
Josh Boyer 9cb73b
+		.match			= type->match,
Josh Boyer 9cb73b
+		.match_data		= description,
Josh Boyer 9cb73b
+		.flags			= KEYRING_SEARCH_LOOKUP_DIRECT,
Josh Boyer 9cb73b
+	};
Josh Boyer 9cb73b
 	struct key *key;
Josh Boyer 9cb73b
 	key_ref_t key_ref;
Josh Boyer 9cb73b
 	int ret;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	kenter("%s,%s,%p,%zu,%p,%p,%lx",
Josh Boyer 9cb73b
-	       type->name, description, callout_info, callout_len, aux,
Josh Boyer 9cb73b
-	       dest_keyring, flags);
Josh Boyer 9cb73b
+	       ctx.index_key.type->name, ctx.index_key.description,
Josh Boyer 9cb73b
+	       callout_info, callout_len, aux, dest_keyring, flags);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	/* search all the process keyrings for a key */
Josh Boyer 9cb73b
-	key_ref = search_process_keyrings(type, description, type->match,
Josh Boyer 9cb73b
-					  false, cred);
Josh Boyer 9cb73b
+	key_ref = search_process_keyrings(&ctx;;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	if (!IS_ERR(key_ref)) {
Josh Boyer 9cb73b
 		key = key_ref_to_ptr(key_ref);
Josh Boyer 9cb73b
@@ -568,9 +567,8 @@ struct key *request_key_and_link(struct key_type *type,
Josh Boyer 9cb73b
 		if (!callout_info)
Josh Boyer 9cb73b
 			goto error;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-		key = construct_key_and_link(type, description, callout_info,
Josh Boyer 9cb73b
-					     callout_len, aux, dest_keyring,
Josh Boyer 9cb73b
-					     flags);
Josh Boyer 9cb73b
+		key = construct_key_and_link(&ctx, callout_info, callout_len,
Josh Boyer 9cb73b
+					     aux, dest_keyring, flags);
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 error:
Josh Boyer 9cb73b
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
Josh Boyer 9cb73b
index 92077de..8d09852 100644
Josh Boyer 9cb73b
--- a/security/keys/request_key_auth.c
Josh Boyer 9cb73b
+++ b/security/keys/request_key_auth.c
Josh Boyer 9cb73b
@@ -239,15 +239,17 @@ static int key_get_instantiation_authkey_match(const struct key *key,
Josh Boyer 9cb73b
  */
Josh Boyer 9cb73b
 struct key *key_get_instantiation_authkey(key_serial_t target_id)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
-	const struct cred *cred = current_cred();
Josh Boyer 9cb73b
+	struct keyring_search_context ctx = {
Josh Boyer 9cb73b
+		.index_key.type		= &key_type_request_key_auth,
Josh Boyer 9cb73b
+		.cred			= current_cred(),
Josh Boyer 9cb73b
+		.match			= key_get_instantiation_authkey_match,
Josh Boyer 9cb73b
+		.match_data		= (void *)(unsigned long)target_id,
Josh Boyer 9cb73b
+		.flags			= KEYRING_SEARCH_LOOKUP_DIRECT,
Josh Boyer 9cb73b
+	};
Josh Boyer 9cb73b
 	struct key *authkey;
Josh Boyer 9cb73b
 	key_ref_t authkey_ref;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	authkey_ref = search_process_keyrings(
Josh Boyer 9cb73b
-		&key_type_request_key_auth,
Josh Boyer 9cb73b
-		(void *) (unsigned long) target_id,
Josh Boyer 9cb73b
-		key_get_instantiation_authkey_match,
Josh Boyer 9cb73b
-		false, cred);
Josh Boyer 9cb73b
+	authkey_ref = search_process_keyrings(&ctx;;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	if (IS_ERR(authkey_ref)) {
Josh Boyer 9cb73b
 		authkey = ERR_CAST(authkey_ref);
Josh Boyer 9cb73b
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
Josh Boyer 9cb73b
index 55dc889..faa2cae 100644
Josh Boyer 9cb73b
--- a/security/keys/user_defined.c
Josh Boyer 9cb73b
+++ b/security/keys/user_defined.c
Josh Boyer 9cb73b
@@ -25,14 +25,15 @@ static int logon_vet_description(const char *desc);
Josh Boyer 9cb73b
  * arbitrary blob of data as the payload
Josh Boyer 9cb73b
  */
Josh Boyer 9cb73b
 struct key_type key_type_user = {
Josh Boyer 9cb73b
-	.name		= "user",
Josh Boyer 9cb73b
-	.instantiate	= user_instantiate,
Josh Boyer 9cb73b
-	.update		= user_update,
Josh Boyer 9cb73b
-	.match		= user_match,
Josh Boyer 9cb73b
-	.revoke		= user_revoke,
Josh Boyer 9cb73b
-	.destroy	= user_destroy,
Josh Boyer 9cb73b
-	.describe	= user_describe,
Josh Boyer 9cb73b
-	.read		= user_read,
Josh Boyer 9cb73b
+	.name			= "user",
Josh Boyer 9cb73b
+	.def_lookup_type	= KEYRING_SEARCH_LOOKUP_DIRECT,
Josh Boyer 9cb73b
+	.instantiate		= user_instantiate,
Josh Boyer 9cb73b
+	.update			= user_update,
Josh Boyer 9cb73b
+	.match			= user_match,
Josh Boyer 9cb73b
+	.revoke			= user_revoke,
Josh Boyer 9cb73b
+	.destroy		= user_destroy,
Josh Boyer 9cb73b
+	.describe		= user_describe,
Josh Boyer 9cb73b
+	.read			= user_read,
Josh Boyer 9cb73b
 };
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 EXPORT_SYMBOL_GPL(key_type_user);
Josh Boyer 9cb73b
@@ -45,6 +46,7 @@ EXPORT_SYMBOL_GPL(key_type_user);
Josh Boyer 9cb73b
  */
Josh Boyer 9cb73b
 struct key_type key_type_logon = {
Josh Boyer 9cb73b
 	.name			= "logon",
Josh Boyer 9cb73b
+	.def_lookup_type	= KEYRING_SEARCH_LOOKUP_DIRECT,
Josh Boyer 9cb73b
 	.instantiate		= user_instantiate,
Josh Boyer 9cb73b
 	.update			= user_update,
Josh Boyer 9cb73b
 	.match			= user_match,
Josh Boyer 9cb73b
-- 
Josh Boyer 9cb73b
1.8.3.1
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Josh Boyer 9cb73b
From 4dffed72b92a305bcdbb73b719570d8f4ec53f46 Mon Sep 17 00:00:00 2001
Josh Boyer 9cb73b
From: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
Date: Fri, 30 Aug 2013 15:37:52 +0100
Josh Boyer 9cb73b
Subject: [PATCH 06/10] KEYS: Search for auth-key by name rather than target
Josh Boyer 9cb73b
 key ID
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Search for auth-key by name rather than by target key ID as, in a future
Josh Boyer 9cb73b
patch, we'll by searching directly by index key in preference to iteration
Josh Boyer 9cb73b
over all keys.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Signed-off-by: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
---
Josh Boyer 9cb73b
 security/keys/request_key_auth.c | 21 +++++++--------------
Josh Boyer 9cb73b
 1 file changed, 7 insertions(+), 14 deletions(-)
Josh Boyer 9cb73b
Josh Boyer 9cb73b
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
Josh Boyer 9cb73b
index 8d09852..7495a93 100644
Josh Boyer 9cb73b
--- a/security/keys/request_key_auth.c
Josh Boyer 9cb73b
+++ b/security/keys/request_key_auth.c
Josh Boyer 9cb73b
@@ -18,6 +18,7 @@
Josh Boyer 9cb73b
 #include <linux/slab.h>
Josh Boyer 9cb73b
 #include <asm/uaccess.h>
Josh Boyer 9cb73b
 #include "internal.h"
Josh Boyer 9cb73b
+#include <keys/user-type.h>
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 static int request_key_auth_instantiate(struct key *,
Josh Boyer 9cb73b
 					struct key_preparsed_payload *);
Josh Boyer 9cb73b
@@ -222,33 +223,25 @@ error_alloc:
Josh Boyer 9cb73b
 }
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 /*
Josh Boyer 9cb73b
- * See if an authorisation key is associated with a particular key.
Josh Boyer 9cb73b
- */
Josh Boyer 9cb73b
-static int key_get_instantiation_authkey_match(const struct key *key,
Josh Boyer 9cb73b
-					       const void *_id)
Josh Boyer 9cb73b
-{
Josh Boyer 9cb73b
-	struct request_key_auth *rka = key->payload.data;
Josh Boyer 9cb73b
-	key_serial_t id = (key_serial_t)(unsigned long) _id;
Josh Boyer 9cb73b
-
Josh Boyer 9cb73b
-	return rka->target_key->serial == id;
Josh Boyer 9cb73b
-}
Josh Boyer 9cb73b
-
Josh Boyer 9cb73b
-/*
Josh Boyer 9cb73b
  * Search the current process's keyrings for the authorisation key for
Josh Boyer 9cb73b
  * instantiation of a key.
Josh Boyer 9cb73b
  */
Josh Boyer 9cb73b
 struct key *key_get_instantiation_authkey(key_serial_t target_id)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
+	char description[16];
Josh Boyer 9cb73b
 	struct keyring_search_context ctx = {
Josh Boyer 9cb73b
 		.index_key.type		= &key_type_request_key_auth,
Josh Boyer 9cb73b
+		.index_key.description	= description,
Josh Boyer 9cb73b
 		.cred			= current_cred(),
Josh Boyer 9cb73b
-		.match			= key_get_instantiation_authkey_match,
Josh Boyer 9cb73b
-		.match_data		= (void *)(unsigned long)target_id,
Josh Boyer 9cb73b
+		.match			= user_match,
Josh Boyer 9cb73b
+		.match_data		= description,
Josh Boyer 9cb73b
 		.flags			= KEYRING_SEARCH_LOOKUP_DIRECT,
Josh Boyer 9cb73b
 	};
Josh Boyer 9cb73b
 	struct key *authkey;
Josh Boyer 9cb73b
 	key_ref_t authkey_ref;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
+	sprintf(description, "%x", target_id);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
 	authkey_ref = search_process_keyrings(&ctx;;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	if (IS_ERR(authkey_ref)) {
Josh Boyer 9cb73b
-- 
Josh Boyer 9cb73b
1.8.3.1
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Josh Boyer 9cb73b
From 5f3c76b0923620ddd5294270ac478819f06f21d1 Mon Sep 17 00:00:00 2001
Josh Boyer 9cb73b
From: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
Date: Fri, 30 Aug 2013 15:37:53 +0100
Josh Boyer 9cb73b
Subject: [PATCH 07/10] KEYS: Define a __key_get() wrapper to use rather than
Josh Boyer 9cb73b
 atomic_inc()
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Define a __key_get() wrapper to use rather than atomic_inc() on the key usage
Josh Boyer 9cb73b
count as this makes it easier to hook in refcount error debugging.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Signed-off-by: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
---
Josh Boyer 9cb73b
 Documentation/security/keys.txt | 13 ++++++++-----
Josh Boyer 9cb73b
 include/linux/key.h             | 10 +++++++---
Josh Boyer 9cb73b
 security/keys/key.c             |  2 +-
Josh Boyer 9cb73b
 security/keys/keyring.c         |  6 +++---
Josh Boyer 9cb73b
 security/keys/process_keys.c    | 16 ++++++++--------
Josh Boyer 9cb73b
 5 files changed, 27 insertions(+), 20 deletions(-)
Josh Boyer 9cb73b
Josh Boyer 9cb73b
diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt
Josh Boyer 9cb73b
index 9ede670..a4c33f1 100644
Josh Boyer 9cb73b
--- a/Documentation/security/keys.txt
Josh Boyer 9cb73b
+++ b/Documentation/security/keys.txt
Josh Boyer 9cb73b
@@ -960,14 +960,17 @@ payload contents" for more information.
Josh Boyer 9cb73b
     the argument will not be parsed.
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-(*) Extra references can be made to a key by calling the following function:
Josh Boyer 9cb73b
+(*) Extra references can be made to a key by calling one of the following
Josh Boyer 9cb73b
+    functions:
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
+	struct key *__key_get(struct key *key);
Josh Boyer 9cb73b
 	struct key *key_get(struct key *key);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-    These need to be disposed of by calling key_put() when they've been
Josh Boyer 9cb73b
-    finished with. The key pointer passed in will be returned. If the pointer
Josh Boyer 9cb73b
-    is NULL or CONFIG_KEYS is not set then the key will not be dereferenced and
Josh Boyer 9cb73b
-    no increment will take place.
Josh Boyer 9cb73b
+    Keys so references will need to be disposed of by calling key_put() when
Josh Boyer 9cb73b
+    they've been finished with.  The key pointer passed in will be returned.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+    In the case of key_get(), if the pointer is NULL or CONFIG_KEYS is not set
Josh Boyer 9cb73b
+    then the key will not be dereferenced and no increment will take place.
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 (*) A key's serial number can be obtained by calling:
Josh Boyer 9cb73b
diff --git a/include/linux/key.h b/include/linux/key.h
Josh Boyer 9cb73b
index d573e82..ef596c7 100644
Josh Boyer 9cb73b
--- a/include/linux/key.h
Josh Boyer 9cb73b
+++ b/include/linux/key.h
Josh Boyer 9cb73b
@@ -219,13 +219,17 @@ extern void key_revoke(struct key *key);
Josh Boyer 9cb73b
 extern void key_invalidate(struct key *key);
Josh Boyer 9cb73b
 extern void key_put(struct key *key);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-static inline struct key *key_get(struct key *key)
Josh Boyer 9cb73b
+static inline struct key *__key_get(struct key *key)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
-	if (key)
Josh Boyer 9cb73b
-		atomic_inc(&key->usage);
Josh Boyer 9cb73b
+	atomic_inc(&key->usage);
Josh Boyer 9cb73b
 	return key;
Josh Boyer 9cb73b
 }
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
+static inline struct key *key_get(struct key *key)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	return key ? __key_get(key) : key;
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
 static inline void key_ref_put(key_ref_t key_ref)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
 	key_put(key_ref_to_ptr(key_ref));
Josh Boyer 9cb73b
diff --git a/security/keys/key.c b/security/keys/key.c
Josh Boyer 9cb73b
index 7e6bc39..1e23cc2 100644
Josh Boyer 9cb73b
--- a/security/keys/key.c
Josh Boyer 9cb73b
+++ b/security/keys/key.c
Josh Boyer 9cb73b
@@ -644,7 +644,7 @@ found:
Josh Boyer 9cb73b
 	/* this races with key_put(), but that doesn't matter since key_put()
Josh Boyer 9cb73b
 	 * doesn't actually change the key
Josh Boyer 9cb73b
 	 */
Josh Boyer 9cb73b
-	atomic_inc(&key->usage);
Josh Boyer 9cb73b
+	__key_get(key);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 error:
Josh Boyer 9cb73b
 	spin_unlock(&key_serial_lock);
Josh Boyer 9cb73b
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
Josh Boyer 9cb73b
index b42f2d4..87eff32 100644
Josh Boyer 9cb73b
--- a/security/keys/keyring.c
Josh Boyer 9cb73b
+++ b/security/keys/keyring.c
Josh Boyer 9cb73b
@@ -479,7 +479,7 @@ not_this_keyring:
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	/* we found a viable match */
Josh Boyer 9cb73b
 found:
Josh Boyer 9cb73b
-	atomic_inc(&key->usage);
Josh Boyer 9cb73b
+	__key_get(key);
Josh Boyer 9cb73b
 	key->last_used_at = ctx->now.tv_sec;
Josh Boyer 9cb73b
 	keyring->last_used_at = ctx->now.tv_sec;
Josh Boyer 9cb73b
 	while (sp > 0)
Josh Boyer 9cb73b
@@ -573,7 +573,7 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref,
Josh Boyer 9cb73b
 	return ERR_PTR(-ENOKEY);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 found:
Josh Boyer 9cb73b
-	atomic_inc(&key->usage);
Josh Boyer 9cb73b
+	__key_get(key);
Josh Boyer 9cb73b
 	keyring->last_used_at = key->last_used_at =
Josh Boyer 9cb73b
 		current_kernel_time().tv_sec;
Josh Boyer 9cb73b
 	rcu_read_unlock();
Josh Boyer 9cb73b
@@ -909,7 +909,7 @@ void __key_link(struct key *keyring, struct key *key,
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	klist = rcu_dereference_locked_keyring(keyring);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-	atomic_inc(&key->usage);
Josh Boyer 9cb73b
+	__key_get(key);
Josh Boyer 9cb73b
 	keyring->last_used_at = key->last_used_at =
Josh Boyer 9cb73b
 		current_kernel_time().tv_sec;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
Josh Boyer 9cb73b
index e68a3e0..68548ea 100644
Josh Boyer 9cb73b
--- a/security/keys/process_keys.c
Josh Boyer 9cb73b
+++ b/security/keys/process_keys.c
Josh Boyer 9cb73b
@@ -235,7 +235,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
Josh Boyer 9cb73b
 		if (IS_ERR(keyring))
Josh Boyer 9cb73b
 			return PTR_ERR(keyring);
Josh Boyer 9cb73b
 	} else {
Josh Boyer 9cb73b
-		atomic_inc(&keyring->usage);
Josh Boyer 9cb73b
+		__key_get(keyring);
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 	/* install the keyring */
Josh Boyer 9cb73b
@@ -544,7 +544,7 @@ try_again:
Josh Boyer 9cb73b
 		}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 		key = ctx.cred->thread_keyring;
Josh Boyer 9cb73b
-		atomic_inc(&key->usage);
Josh Boyer 9cb73b
+		__key_get(key);
Josh Boyer 9cb73b
 		key_ref = make_key_ref(key, 1);
Josh Boyer 9cb73b
 		break;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -562,7 +562,7 @@ try_again:
Josh Boyer 9cb73b
 		}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 		key = ctx.cred->process_keyring;
Josh Boyer 9cb73b
-		atomic_inc(&key->usage);
Josh Boyer 9cb73b
+		__key_get(key);
Josh Boyer 9cb73b
 		key_ref = make_key_ref(key, 1);
Josh Boyer 9cb73b
 		break;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -593,7 +593,7 @@ try_again:
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 		rcu_read_lock();
Josh Boyer 9cb73b
 		key = rcu_dereference(ctx.cred->session_keyring);
Josh Boyer 9cb73b
-		atomic_inc(&key->usage);
Josh Boyer 9cb73b
+		__key_get(key);
Josh Boyer 9cb73b
 		rcu_read_unlock();
Josh Boyer 9cb73b
 		key_ref = make_key_ref(key, 1);
Josh Boyer 9cb73b
 		break;
Josh Boyer 9cb73b
@@ -606,7 +606,7 @@ try_again:
Josh Boyer 9cb73b
 		}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 		key = ctx.cred->user->uid_keyring;
Josh Boyer 9cb73b
-		atomic_inc(&key->usage);
Josh Boyer 9cb73b
+		__key_get(key);
Josh Boyer 9cb73b
 		key_ref = make_key_ref(key, 1);
Josh Boyer 9cb73b
 		break;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -618,7 +618,7 @@ try_again:
Josh Boyer 9cb73b
 		}
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 		key = ctx.cred->user->session_keyring;
Josh Boyer 9cb73b
-		atomic_inc(&key->usage);
Josh Boyer 9cb73b
+		__key_get(key);
Josh Boyer 9cb73b
 		key_ref = make_key_ref(key, 1);
Josh Boyer 9cb73b
 		break;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -632,7 +632,7 @@ try_again:
Josh Boyer 9cb73b
 		if (!key)
Josh Boyer 9cb73b
 			goto error;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
-		atomic_inc(&key->usage);
Josh Boyer 9cb73b
+		__key_get(key);
Josh Boyer 9cb73b
 		key_ref = make_key_ref(key, 1);
Josh Boyer 9cb73b
 		break;
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
@@ -648,7 +648,7 @@ try_again:
Josh Boyer 9cb73b
 		} else {
Josh Boyer 9cb73b
 			rka = ctx.cred->request_key_auth->payload.data;
Josh Boyer 9cb73b
 			key = rka->dest_keyring;
Josh Boyer 9cb73b
-			atomic_inc(&key->usage);
Josh Boyer 9cb73b
+			__key_get(key);
Josh Boyer 9cb73b
 		}
Josh Boyer 9cb73b
 		up_read(&ctx.cred->request_key_auth->sem);
Josh Boyer 9cb73b
 		if (!key)
Josh Boyer 9cb73b
-- 
Josh Boyer 9cb73b
1.8.3.1
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Josh Boyer 9cb73b
From 99b0f3185570bb92a61952673b9933d9c1999508 Mon Sep 17 00:00:00 2001
Josh Boyer 9cb73b
From: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
Date: Fri, 30 Aug 2013 15:37:53 +0100
Josh Boyer 9cb73b
Subject: [PATCH 08/10] KEYS: Drop the permissions argument from
Josh Boyer 9cb73b
 __keyring_search_one()
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Drop the permissions argument from __keyring_search_one() as the only caller
Josh Boyer 9cb73b
passes 0 here - which causes all checks to be skipped.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Signed-off-by: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
---
Josh Boyer 9cb73b
 security/keys/internal.h | 3 +--
Josh Boyer 9cb73b
 security/keys/key.c      | 2 +-
Josh Boyer 9cb73b
 security/keys/keyring.c  | 9 +++------
Josh Boyer 9cb73b
 3 files changed, 5 insertions(+), 9 deletions(-)
Josh Boyer 9cb73b
Josh Boyer 9cb73b
diff --git a/security/keys/internal.h b/security/keys/internal.h
Josh Boyer 9cb73b
index f4bf938..73950bf 100644
Josh Boyer 9cb73b
--- a/security/keys/internal.h
Josh Boyer 9cb73b
+++ b/security/keys/internal.h
Josh Boyer 9cb73b
@@ -99,8 +99,7 @@ extern void __key_link_end(struct key *keyring,
Josh Boyer 9cb73b
 			   unsigned long prealloc);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 extern key_ref_t __keyring_search_one(key_ref_t keyring_ref,
Josh Boyer 9cb73b
-				      const struct keyring_index_key *index_key,
Josh Boyer 9cb73b
-				      key_perm_t perm);
Josh Boyer 9cb73b
+				      const struct keyring_index_key *index_key);
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 extern struct key *keyring_search_instkey(struct key *keyring,
Josh Boyer 9cb73b
 					  key_serial_t target_id);
Josh Boyer 9cb73b
diff --git a/security/keys/key.c b/security/keys/key.c
Josh Boyer 9cb73b
index 1e23cc2..7d716b8 100644
Josh Boyer 9cb73b
--- a/security/keys/key.c
Josh Boyer 9cb73b
+++ b/security/keys/key.c
Josh Boyer 9cb73b
@@ -847,7 +847,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
Josh Boyer 9cb73b
 	 * update that instead if possible
Josh Boyer 9cb73b
 	 */
Josh Boyer 9cb73b
 	if (index_key.type->update) {
Josh Boyer 9cb73b
-		key_ref = __keyring_search_one(keyring_ref, &index_key, 0);
Josh Boyer 9cb73b
+		key_ref = __keyring_search_one(keyring_ref, &index_key);
Josh Boyer 9cb73b
 		if (!IS_ERR(key_ref))
Josh Boyer 9cb73b
 			goto found_matching_key;
Josh Boyer 9cb73b
 	}
Josh Boyer 9cb73b
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
Josh Boyer 9cb73b
index 87eff32..eeef1a0 100644
Josh Boyer 9cb73b
--- a/security/keys/keyring.c
Josh Boyer 9cb73b
+++ b/security/keys/keyring.c
Josh Boyer 9cb73b
@@ -531,15 +531,14 @@ EXPORT_SYMBOL(keyring_search);
Josh Boyer 9cb73b
  * RCU is used to make it unnecessary to lock the keyring key list here.
Josh Boyer 9cb73b
  *
Josh Boyer 9cb73b
  * Returns a pointer to the found key with usage count incremented if
Josh Boyer 9cb73b
- * successful and returns -ENOKEY if not found.  Revoked keys and keys not
Josh Boyer 9cb73b
- * providing the requested permission are skipped over.
Josh Boyer 9cb73b
+ * successful and returns -ENOKEY if not found.  Revoked and invalidated keys
Josh Boyer 9cb73b
+ * are skipped over.
Josh Boyer 9cb73b
  *
Josh Boyer 9cb73b
  * If successful, the possession indicator is propagated from the keyring ref
Josh Boyer 9cb73b
  * to the returned key reference.
Josh Boyer 9cb73b
  */
Josh Boyer 9cb73b
 key_ref_t __keyring_search_one(key_ref_t keyring_ref,
Josh Boyer 9cb73b
-			       const struct keyring_index_key *index_key,
Josh Boyer 9cb73b
-			       key_perm_t perm)
Josh Boyer 9cb73b
+			       const struct keyring_index_key *index_key)
Josh Boyer 9cb73b
 {
Josh Boyer 9cb73b
 	struct keyring_list *klist;
Josh Boyer 9cb73b
 	struct key *keyring, *key;
Josh Boyer 9cb73b
@@ -560,8 +559,6 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref,
Josh Boyer 9cb73b
 			if (key->type == index_key->type &&
Josh Boyer 9cb73b
 			    (!key->type->match ||
Josh Boyer 9cb73b
 			     key->type->match(key, index_key->description)) &&
Josh Boyer 9cb73b
-			    key_permission(make_key_ref(key, possessed),
Josh Boyer 9cb73b
-					   perm) == 0 &&
Josh Boyer 9cb73b
 			    !(key->flags & ((1 << KEY_FLAG_INVALIDATED) |
Josh Boyer 9cb73b
 					    (1 << KEY_FLAG_REVOKED)))
Josh Boyer 9cb73b
 			    )
Josh Boyer 9cb73b
-- 
Josh Boyer 9cb73b
1.8.3.1
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Josh Boyer 9cb73b
From cb720b39e41e62d55bf1e5f8243d78643d31154d Mon Sep 17 00:00:00 2001
Josh Boyer 9cb73b
From: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
Date: Fri, 30 Aug 2013 15:37:53 +0100
Josh Boyer 9cb73b
Subject: [PATCH 09/10] Add a generic associative array implementation.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Add a generic associative array implementation that can be used as the
Josh Boyer 9cb73b
container for keyrings, thereby massively increasing the capacity available
Josh Boyer 9cb73b
whilst also speeding up searching in keyrings that contain a lot of keys.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
This may also be useful in FS-Cache for tracking cookies.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Documentation is added into Documentation/associative_array.txt
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Some of the properties of the implementation are:
Josh Boyer 9cb73b
Josh Boyer 9cb73b
 (1) Objects are opaque pointers.  The implementation does not care where they
Josh Boyer 9cb73b
     point (if anywhere) or what they point to (if anything).
Josh Boyer 9cb73b
Josh Boyer 9cb73b
     [!] NOTE: Pointers to objects _must_ be zero in the two least significant
Josh Boyer 9cb73b
     	       bits.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
 (2) Objects do not need to contain linkage blocks for use by the array.  This
Josh Boyer 9cb73b
     permits an object to be located in multiple arrays simultaneously.
Josh Boyer 9cb73b
     Rather, the array is made up of metadata blocks that point to objects.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
 (3) Objects are labelled as being one of two types (the type is a bool value).
Josh Boyer 9cb73b
     This information is stored in the array, but has no consequence to the
Josh Boyer 9cb73b
     array itself or its algorithms.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
 (4) Objects require index keys to locate them within the array.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
 (5) Index keys must be unique.  Inserting an object with the same key as one
Josh Boyer 9cb73b
     already in the array will replace the old object.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
 (6) Index keys can be of any length and can be of different lengths.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
 (7) Index keys should encode the length early on, before any variation due to
Josh Boyer 9cb73b
     length is seen.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
 (8) Index keys can include a hash to scatter objects throughout the array.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
 (9) The array can iterated over.  The objects will not necessarily come out in
Josh Boyer 9cb73b
     key order.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
(10) The array can be iterated whilst it is being modified, provided the RCU
Josh Boyer 9cb73b
     readlock is being held by the iterator.  Note, however, under these
Josh Boyer 9cb73b
     circumstances, some objects may be seen more than once.  If this is a
Josh Boyer 9cb73b
     problem, the iterator should lock against modification.  Objects will not
Josh Boyer 9cb73b
     be missed, however, unless deleted.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
(11) Objects in the array can be looked up by means of their index key.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
(12) Objects can be looked up whilst the array is being modified, provided the
Josh Boyer 9cb73b
     RCU readlock is being held by the thread doing the look up.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
The implementation uses a tree of 16-pointer nodes internally that are indexed
Josh Boyer 9cb73b
on each level by nibbles from the index key.  To improve memory efficiency,
Josh Boyer 9cb73b
shortcuts can be emplaced to skip over what would otherwise be a series of
Josh Boyer 9cb73b
single-occupancy nodes.  Further, nodes pack leaf object pointers into spare
Josh Boyer 9cb73b
space in the node rather than making an extra branch until as such time an
Josh Boyer 9cb73b
object needs to be added to a full node.
Josh Boyer 9cb73b
Josh Boyer 9cb73b
Signed-off-by: David Howells <dhowells@redhat.com>
Josh Boyer 9cb73b
---
Josh Boyer 9cb73b
 Documentation/assoc_array.txt    |  574 +++++++++++++
Josh Boyer 9cb73b
 include/linux/assoc_array.h      |   92 ++
Josh Boyer 9cb73b
 include/linux/assoc_array_priv.h |  182 ++++
Josh Boyer 9cb73b
 lib/Kconfig                      |   14 +
Josh Boyer 9cb73b
 lib/Makefile                     |    1 +
Josh Boyer 9cb73b
 lib/assoc_array.c                | 1745 ++++++++++++++++++++++++++++++++++++++
Josh Boyer 9cb73b
 6 files changed, 2608 insertions(+)
Josh Boyer 9cb73b
 create mode 100644 Documentation/assoc_array.txt
Josh Boyer 9cb73b
 create mode 100644 include/linux/assoc_array.h
Josh Boyer 9cb73b
 create mode 100644 include/linux/assoc_array_priv.h
Josh Boyer 9cb73b
 create mode 100644 lib/assoc_array.c
Josh Boyer 9cb73b
Josh Boyer 9cb73b
diff --git a/Documentation/assoc_array.txt b/Documentation/assoc_array.txt
Josh Boyer 9cb73b
new file mode 100644
Josh Boyer 9cb73b
index 0000000..f4faec0
Josh Boyer 9cb73b
--- /dev/null
Josh Boyer 9cb73b
+++ b/Documentation/assoc_array.txt
Josh Boyer 9cb73b
@@ -0,0 +1,574 @@
Josh Boyer 9cb73b
+		   ========================================
Josh Boyer 9cb73b
+		   GENERIC ASSOCIATIVE ARRAY IMPLEMENTATION
Josh Boyer 9cb73b
+		   ========================================
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+Contents:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ - Overview.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ - The public API.
Josh Boyer 9cb73b
+   - Edit script.
Josh Boyer 9cb73b
+   - Operations table.
Josh Boyer 9cb73b
+   - Manipulation functions.
Josh Boyer 9cb73b
+   - Access functions.
Josh Boyer 9cb73b
+   - Index key form.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ - Internal workings.
Josh Boyer 9cb73b
+   - Basic internal tree layout.
Josh Boyer 9cb73b
+   - Shortcuts.
Josh Boyer 9cb73b
+   - Splitting and collapsing nodes.
Josh Boyer 9cb73b
+   - Non-recursive iteration.
Josh Boyer 9cb73b
+   - Simultaneous alteration and iteration.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+========
Josh Boyer 9cb73b
+OVERVIEW
Josh Boyer 9cb73b
+========
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+This associative array implementation is an object container with the following
Josh Boyer 9cb73b
+properties:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (1) Objects are opaque pointers.  The implementation does not care where they
Josh Boyer 9cb73b
+     point (if anywhere) or what they point to (if anything).
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     [!] NOTE: Pointers to objects _must_ be zero in the least significant bit.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (2) Objects do not need to contain linkage blocks for use by the array.  This
Josh Boyer 9cb73b
+     permits an object to be located in multiple arrays simultaneously.
Josh Boyer 9cb73b
+     Rather, the array is made up of metadata blocks that point to objects.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (3) Objects require index keys to locate them within the array.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (4) Index keys must be unique.  Inserting an object with the same key as one
Josh Boyer 9cb73b
+     already in the array will replace the old object.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (5) Index keys can be of any length and can be of different lengths.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (6) Index keys should encode the length early on, before any variation due to
Josh Boyer 9cb73b
+     length is seen.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (7) Index keys can include a hash to scatter objects throughout the array.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (8) The array can iterated over.  The objects will not necessarily come out in
Josh Boyer 9cb73b
+     key order.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (9) The array can be iterated over whilst it is being modified, provided the
Josh Boyer 9cb73b
+     RCU readlock is being held by the iterator.  Note, however, under these
Josh Boyer 9cb73b
+     circumstances, some objects may be seen more than once.  If this is a
Josh Boyer 9cb73b
+     problem, the iterator should lock against modification.  Objects will not
Josh Boyer 9cb73b
+     be missed, however, unless deleted.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+(10) Objects in the array can be looked up by means of their index key.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+(11) Objects can be looked up whilst the array is being modified, provided the
Josh Boyer 9cb73b
+     RCU readlock is being held by the thread doing the look up.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+The implementation uses a tree of 16-pointer nodes internally that are indexed
Josh Boyer 9cb73b
+on each level by nibbles from the index key in the same manner as in a radix
Josh Boyer 9cb73b
+tree.  To improve memory efficiency, shortcuts can be emplaced to skip over
Josh Boyer 9cb73b
+what would otherwise be a series of single-occupancy nodes.  Further, nodes
Josh Boyer 9cb73b
+pack leaf object pointers into spare space in the node rather than making an
Josh Boyer 9cb73b
+extra branch until as such time an object needs to be added to a full node.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+==============
Josh Boyer 9cb73b
+THE PUBLIC API
Josh Boyer 9cb73b
+==============
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+The public API can be found in <linux/assoc_array.h>.  The associative array is
Josh Boyer 9cb73b
+rooted on the following structure:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	struct assoc_array {
Josh Boyer 9cb73b
+		...
Josh Boyer 9cb73b
+	};
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+The code is selected by enabling CONFIG_ASSOCIATIVE_ARRAY.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+EDIT SCRIPT
Josh Boyer 9cb73b
+-----------
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+The insertion and deletion functions produce an 'edit script' that can later be
Josh Boyer 9cb73b
+applied to effect the changes without risking ENOMEM.  This retains the
Josh Boyer 9cb73b
+preallocated metadata blocks that will be installed in the internal tree and
Josh Boyer 9cb73b
+keeps track of the metadata blocks that will be removed from the tree when the
Josh Boyer 9cb73b
+script is applied.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+This is also used to keep track of dead blocks and dead objects after the
Josh Boyer 9cb73b
+script has been applied so that they can be freed later.  The freeing is done
Josh Boyer 9cb73b
+after an RCU grace period has passed - thus allowing access functions to
Josh Boyer 9cb73b
+proceed under the RCU read lock.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+The script appears as outside of the API as a pointer of the type:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	struct assoc_array_edit;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+There are two functions for dealing with the script:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (1) Apply an edit script.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	void assoc_array_apply_edit(struct assoc_array_edit *edit);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     This will perform the edit functions, interpolating various write barriers
Josh Boyer 9cb73b
+     to permit accesses under the RCU read lock to continue.  The edit script
Josh Boyer 9cb73b
+     will then be passed to call_rcu() to free it and any dead stuff it points
Josh Boyer 9cb73b
+     to.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (2) Cancel an edit script.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	void assoc_array_cancel_edit(struct assoc_array_edit *edit);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     This frees the edit script and all preallocated memory immediately.  If
Josh Boyer 9cb73b
+     this was for insertion, the new object is _not_ released by this function,
Josh Boyer 9cb73b
+     but must rather be released by the caller.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+These functions are guaranteed not to fail.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+OPERATIONS TABLE
Josh Boyer 9cb73b
+----------------
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+Various functions take a table of operations:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	struct assoc_array_ops {
Josh Boyer 9cb73b
+		...
Josh Boyer 9cb73b
+	};
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+This points to a number of methods, all of which need to be provided:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (1) Get a chunk of index key from caller data:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	unsigned long (*get_key_chunk)(const void *index_key, int level);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     This should return a chunk of caller-supplied index key starting at the
Josh Boyer 9cb73b
+     *bit* position given by the level argument.  The level argument will be a
Josh Boyer 9cb73b
+     multiple of ASSOC_ARRAY_KEY_CHUNK_SIZE and the function should return
Josh Boyer 9cb73b
+     ASSOC_ARRAY_KEY_CHUNK_SIZE bits.  No error is possible.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (2) Get a chunk of an object's index key.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	unsigned long (*get_object_key_chunk)(const void *object, int level);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     As the previous function, but gets its data from an object in the array
Josh Boyer 9cb73b
+     rather than from a caller-supplied index key.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (3) See if this is the object we're looking for.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	bool (*compare_object)(const void *object, const void *index_key);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     Compare the object against an index key and return true if it matches and
Josh Boyer 9cb73b
+     false if it doesn't.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (4) Diff the index keys of two objects.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	int (*diff_objects)(const void *a, const void *b);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     Return the bit position at which the index keys of two objects differ or
Josh Boyer 9cb73b
+     -1 if they are the same.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (5) Free an object.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	void (*free_object)(void *object);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     Free the specified object.  Note that this may be called an RCU grace
Josh Boyer 9cb73b
+     period after assoc_array_apply_edit() was called, so synchronize_rcu() may
Josh Boyer 9cb73b
+     be necessary on module unloading.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+MANIPULATION FUNCTIONS
Josh Boyer 9cb73b
+----------------------
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+There are a number of functions for manipulating an associative array:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (1) Initialise an associative array.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	void assoc_array_init(struct assoc_array *array);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     This initialises the base structure for an associative array.  It can't
Josh Boyer 9cb73b
+     fail.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (2) Insert/replace an object in an associative array.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	struct assoc_array_edit *
Josh Boyer 9cb73b
+	assoc_array_insert(struct assoc_array *array,
Josh Boyer 9cb73b
+			   const struct assoc_array_ops *ops,
Josh Boyer 9cb73b
+			   const void *index_key,
Josh Boyer 9cb73b
+			   void *object);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     This inserts the given object into the array.  Note that the least
Josh Boyer 9cb73b
+     significant bit of the pointer must be zero as it's used to type-mark
Josh Boyer 9cb73b
+     pointers internally.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     If an object already exists for that key then it will be replaced with the
Josh Boyer 9cb73b
+     new object and the old one will be freed automatically.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     The index_key argument should hold index key information and is
Josh Boyer 9cb73b
+     passed to the methods in the ops table when they are called.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     This function makes no alteration to the array itself, but rather returns
Josh Boyer 9cb73b
+     an edit script that must be applied.  -ENOMEM is returned in the case of
Josh Boyer 9cb73b
+     an out-of-memory error.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     The caller should lock exclusively against other modifiers of the array.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (3) Delete an object from an associative array.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	struct assoc_array_edit *
Josh Boyer 9cb73b
+	assoc_array_delete(struct assoc_array *array,
Josh Boyer 9cb73b
+			   const struct assoc_array_ops *ops,
Josh Boyer 9cb73b
+			   const void *index_key);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     This deletes an object that matches the specified data from the array.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     The index_key argument should hold index key information and is
Josh Boyer 9cb73b
+     passed to the methods in the ops table when they are called.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     This function makes no alteration to the array itself, but rather returns
Josh Boyer 9cb73b
+     an edit script that must be applied.  -ENOMEM is returned in the case of
Josh Boyer 9cb73b
+     an out-of-memory error.  NULL will be returned if the specified object is
Josh Boyer 9cb73b
+     not found within the array.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     The caller should lock exclusively against other modifiers of the array.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (4) Delete all objects from an associative array.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	struct assoc_array_edit *
Josh Boyer 9cb73b
+	assoc_array_clear(struct assoc_array *array,
Josh Boyer 9cb73b
+			  const struct assoc_array_ops *ops);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     This deletes all the objects from an associative array and leaves it
Josh Boyer 9cb73b
+     completely empty.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     This function makes no alteration to the array itself, but rather returns
Josh Boyer 9cb73b
+     an edit script that must be applied.  -ENOMEM is returned in the case of
Josh Boyer 9cb73b
+     an out-of-memory error.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     The caller should lock exclusively against other modifiers of the array.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (5) Destroy an associative array, deleting all objects.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	void assoc_array_destroy(struct assoc_array *array,
Josh Boyer 9cb73b
+				 const struct assoc_array_ops *ops);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     This destroys the contents of the associative array and leaves it
Josh Boyer 9cb73b
+     completely empty.  It is not permitted for another thread to be traversing
Josh Boyer 9cb73b
+     the array under the RCU read lock at the same time as this function is
Josh Boyer 9cb73b
+     destroying it as no RCU deferral is performed on memory release -
Josh Boyer 9cb73b
+     something that would require memory to be allocated.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     The caller should lock exclusively against other modifiers and accessors
Josh Boyer 9cb73b
+     of the array.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (6) Garbage collect an associative array.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	int assoc_array_gc(struct assoc_array *array,
Josh Boyer 9cb73b
+			   const struct assoc_array_ops *ops,
Josh Boyer 9cb73b
+			   bool (*iterator)(void *object, void *iterator_data),
Josh Boyer 9cb73b
+			   void *iterator_data);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     This iterates over the objects in an associative array and passes each one
Josh Boyer 9cb73b
+     to iterator().  If iterator() returns true, the object is kept.  If it
Josh Boyer 9cb73b
+     returns false, the object will be freed.  If the iterator() function
Josh Boyer 9cb73b
+     returns true, it must perform any appropriate refcount incrementing on the
Josh Boyer 9cb73b
+     object before returning.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     The internal tree will be packed down if possible as part of the iteration
Josh Boyer 9cb73b
+     to reduce the number of nodes in it.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     The iterator_data is passed directly to iterator() and is otherwise
Josh Boyer 9cb73b
+     ignored by the function.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     The function will return 0 if successful and -ENOMEM if there wasn't
Josh Boyer 9cb73b
+     enough memory.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     It is possible for other threads to iterate over or search the array under
Josh Boyer 9cb73b
+     the RCU read lock whilst this function is in progress.  The caller should
Josh Boyer 9cb73b
+     lock exclusively against other modifiers of the array.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ACCESS FUNCTIONS
Josh Boyer 9cb73b
+----------------
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+There are two functions for accessing an associative array:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (1) Iterate over all the objects in an associative array.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	int assoc_array_iterate(const struct assoc_array *array,
Josh Boyer 9cb73b
+				int (*iterator)(const void *object,
Josh Boyer 9cb73b
+						void *iterator_data),
Josh Boyer 9cb73b
+				void *iterator_data);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     This passes each object in the array to the iterator callback function.
Josh Boyer 9cb73b
+     iterator_data is private data for that function.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     This may be used on an array at the same time as the array is being
Josh Boyer 9cb73b
+     modified, provided the RCU read lock is held.  Under such circumstances,
Josh Boyer 9cb73b
+     it is possible for the iteration function to see some objects twice.  If
Josh Boyer 9cb73b
+     this is a problem, then modification should be locked against.  The
Josh Boyer 9cb73b
+     iteration algorithm should not, however, miss any objects.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     The function will return 0 if no objects were in the array or else it will
Josh Boyer 9cb73b
+     return the result of the last iterator function called.  Iteration stops
Josh Boyer 9cb73b
+     immediately if any call to the iteration function results in a non-zero
Josh Boyer 9cb73b
+     return.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (2) Find an object in an associative array.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	void *assoc_array_find(const struct assoc_array *array,
Josh Boyer 9cb73b
+			       const struct assoc_array_ops *ops,
Josh Boyer 9cb73b
+			       const void *index_key);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     This walks through the array's internal tree directly to the object
Josh Boyer 9cb73b
+     specified by the index key..
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     This may be used on an array at the same time as the array is being
Josh Boyer 9cb73b
+     modified, provided the RCU read lock is held.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     The function will return the object if found (and set *_type to the object
Josh Boyer 9cb73b
+     type) or will return NULL if the object was not found.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+INDEX KEY FORM
Josh Boyer 9cb73b
+--------------
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+The index key can be of any form, but since the algorithms aren't told how long
Josh Boyer 9cb73b
+the key is, it is strongly recommended that the index key includes its length
Josh Boyer 9cb73b
+very early on before any variation due to the length would have an effect on
Josh Boyer 9cb73b
+comparisons.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+This will cause leaves with different length keys to scatter away from each
Josh Boyer 9cb73b
+other - and those with the same length keys to cluster together.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+It is also recommended that the index key begin with a hash of the rest of the
Josh Boyer 9cb73b
+key to maximise scattering throughout keyspace.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+The better the scattering, the wider and lower the internal tree will be.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+Poor scattering isn't too much of a problem as there are shortcuts and nodes
Josh Boyer 9cb73b
+can contain mixtures of leaves and metadata pointers.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+The index key is read in chunks of machine word.  Each chunk is subdivided into
Josh Boyer 9cb73b
+one nibble (4 bits) per level, so on a 32-bit CPU this is good for 8 levels and
Josh Boyer 9cb73b
+on a 64-bit CPU, 16 levels.  Unless the scattering is really poor, it is
Josh Boyer 9cb73b
+unlikely that more than one word of any particular index key will have to be
Josh Boyer 9cb73b
+used.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+=================
Josh Boyer 9cb73b
+INTERNAL WORKINGS
Josh Boyer 9cb73b
+=================
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+The associative array data structure has an internal tree.  This tree is
Josh Boyer 9cb73b
+constructed of two types of metadata blocks: nodes and shortcuts.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+A node is an array of slots.  Each slot can contain one of four things:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (*) A NULL pointer, indicating that the slot is empty.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (*) A pointer to an object (a leaf).
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (*) A pointer to a node at the next level.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (*) A pointer to a shortcut.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+BASIC INTERNAL TREE LAYOUT
Josh Boyer 9cb73b
+--------------------------
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+Ignoring shortcuts for the moment, the nodes form a multilevel tree.  The index
Josh Boyer 9cb73b
+key space is strictly subdivided by the nodes in the tree and nodes occur on
Josh Boyer 9cb73b
+fixed levels.  For example:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ Level:	0		1		2		3
Josh Boyer 9cb73b
+	===============	===============	===============	===============
Josh Boyer 9cb73b
+							NODE D
Josh Boyer 9cb73b
+			NODE B		NODE C	+------>+---+
Josh Boyer 9cb73b
+		+------>+---+	+------>+---+	|	| 0 |
Josh Boyer 9cb73b
+	NODE A	|	| 0 |	|	| 0 |	|	+---+
Josh Boyer 9cb73b
+	+---+	|	+---+	|	+---+	|	:   :
Josh Boyer 9cb73b
+	| 0 |	|	:   :	|	:   :	|	+---+
Josh Boyer 9cb73b
+	+---+	|	+---+	|	+---+	|	| f |
Josh Boyer 9cb73b
+	| 1 |---+	| 3 |---+	| 7 |---+	+---+
Josh Boyer 9cb73b
+	+---+		+---+		+---+
Josh Boyer 9cb73b
+	:   :		:   :		| 8 |---+
Josh Boyer 9cb73b
+	+---+		+---+		+---+	|	NODE E
Josh Boyer 9cb73b
+	| e |---+	| f |		:   :   +------>+---+
Josh Boyer 9cb73b
+	+---+	|	+---+		+---+		| 0 |
Josh Boyer 9cb73b
+	| f |	|			| f |		+---+
Josh Boyer 9cb73b
+	+---+	|			+---+		:   :
Josh Boyer 9cb73b
+		|	NODE F				+---+
Josh Boyer 9cb73b
+		+------>+---+				| f |
Josh Boyer 9cb73b
+			| 0 |		NODE G		+---+
Josh Boyer 9cb73b
+			+---+	+------>+---+
Josh Boyer 9cb73b
+			:   :	|	| 0 |
Josh Boyer 9cb73b
+			+---+	|	+---+
Josh Boyer 9cb73b
+			| 6 |---+	:   :
Josh Boyer 9cb73b
+			+---+		+---+
Josh Boyer 9cb73b
+			:   :		| f |
Josh Boyer 9cb73b
+			+---+		+---+
Josh Boyer 9cb73b
+			| f |
Josh Boyer 9cb73b
+			+---+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+In the above example, there are 7 nodes (A-G), each with 16 slots (0-f).
Josh Boyer 9cb73b
+Assuming no other meta data nodes in the tree, the key space is divided thusly:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	KEY PREFIX	NODE
Josh Boyer 9cb73b
+	==========	====
Josh Boyer 9cb73b
+	137*		D
Josh Boyer 9cb73b
+	138*		E
Josh Boyer 9cb73b
+	13[0-69-f]*	C
Josh Boyer 9cb73b
+	1[0-24-f]*	B
Josh Boyer 9cb73b
+	e6*		G
Josh Boyer 9cb73b
+	e[0-57-f]*	F
Josh Boyer 9cb73b
+	[02-df]*	A
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+So, for instance, keys with the following example index keys will be found in
Josh Boyer 9cb73b
+the appropriate nodes:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	INDEX KEY	PREFIX	NODE
Josh Boyer 9cb73b
+	===============	=======	====
Josh Boyer 9cb73b
+	13694892892489	13	C
Josh Boyer 9cb73b
+	13795289025897	137	D
Josh Boyer 9cb73b
+	13889dde88793	138	E
Josh Boyer 9cb73b
+	138bbb89003093	138	E
Josh Boyer 9cb73b
+	1394879524789	12	C
Josh Boyer 9cb73b
+	1458952489	1	B
Josh Boyer 9cb73b
+	9431809de993ba	-	A
Josh Boyer 9cb73b
+	b4542910809cd	-	A
Josh Boyer 9cb73b
+	e5284310def98	e	F
Josh Boyer 9cb73b
+	e68428974237	e6	G
Josh Boyer 9cb73b
+	e7fffcbd443	e	F
Josh Boyer 9cb73b
+	f3842239082	-	A
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+To save memory, if a node can hold all the leaves in its portion of keyspace,
Josh Boyer 9cb73b
+then the node will have all those leaves in it and will not have any metadata
Josh Boyer 9cb73b
+pointers - even if some of those leaves would like to be in the same slot.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+A node can contain a heterogeneous mix of leaves and metadata pointers.
Josh Boyer 9cb73b
+Metadata pointers must be in the slots that match their subdivisions of key
Josh Boyer 9cb73b
+space.  The leaves can be in any slot not occupied by a metadata pointer.  It
Josh Boyer 9cb73b
+is guaranteed that none of the leaves in a node will match a slot occupied by a
Josh Boyer 9cb73b
+metadata pointer.  If the metadata pointer is there, any leaf whose key matches
Josh Boyer 9cb73b
+the metadata key prefix must be in the subtree that the metadata pointer points
Josh Boyer 9cb73b
+to.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+In the above example list of index keys, node A will contain:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	SLOT	CONTENT		INDEX KEY (PREFIX)
Josh Boyer 9cb73b
+	====	===============	==================
Josh Boyer 9cb73b
+	1	PTR TO NODE B	1*
Josh Boyer 9cb73b
+	any	LEAF		9431809de993ba
Josh Boyer 9cb73b
+	any	LEAF		b4542910809cd
Josh Boyer 9cb73b
+	e	PTR TO NODE F	e*
Josh Boyer 9cb73b
+	any	LEAF		f3842239082
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+and node B:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	3	PTR TO NODE C	13*
Josh Boyer 9cb73b
+	any	LEAF		1458952489
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+SHORTCUTS
Josh Boyer 9cb73b
+---------
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+Shortcuts are metadata records that jump over a piece of keyspace.  A shortcut
Josh Boyer 9cb73b
+is a replacement for a series of single-occupancy nodes ascending through the
Josh Boyer 9cb73b
+levels.  Shortcuts exist to save memory and to speed up traversal.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+It is possible for the root of the tree to be a shortcut - say, for example,
Josh Boyer 9cb73b
+the tree contains at least 17 nodes all with key prefix '1111'.  The insertion
Josh Boyer 9cb73b
+algorithm will insert a shortcut to skip over the '1111' keyspace in a single
Josh Boyer 9cb73b
+bound and get to the fourth level where these actually become different.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+SPLITTING AND COLLAPSING NODES
Josh Boyer 9cb73b
+------------------------------
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+Each node has a maximum capacity of 16 leaves and metadata pointers.  If the
Josh Boyer 9cb73b
+insertion algorithm finds that it is trying to insert a 17th object into a
Josh Boyer 9cb73b
+node, that node will be split such that at least two leaves that have a common
Josh Boyer 9cb73b
+key segment at that level end up in a separate node rooted on that slot for
Josh Boyer 9cb73b
+that common key segment.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+If the leaves in a full node and the leaf that is being inserted are
Josh Boyer 9cb73b
+sufficiently similar, then a shortcut will be inserted into the tree.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+When the number of objects in the subtree rooted at a node falls to 16 or
Josh Boyer 9cb73b
+fewer, then the subtree will be collapsed down to a single node - and this will
Josh Boyer 9cb73b
+ripple towards the root if possible.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+NON-RECURSIVE ITERATION
Josh Boyer 9cb73b
+-----------------------
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+Each node and shortcut contains a back pointer to its parent and the number of
Josh Boyer 9cb73b
+slot in that parent that points to it.  None-recursive iteration uses these to
Josh Boyer 9cb73b
+proceed rootwards through the tree, going to the parent node, slot N + 1 to
Josh Boyer 9cb73b
+make sure progress is made without the need for a stack.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+The backpointers, however, make simultaneous alteration and iteration tricky.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+SIMULTANEOUS ALTERATION AND ITERATION
Josh Boyer 9cb73b
+-------------------------------------
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+There are a number of cases to consider:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (1) Simple insert/replace.  This involves simply replacing a NULL or old
Josh Boyer 9cb73b
+     matching leaf pointer with the pointer to the new leaf after a barrier.
Josh Boyer 9cb73b
+     The metadata blocks don't change otherwise.  An old leaf won't be freed
Josh Boyer 9cb73b
+     until after the RCU grace period.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (2) Simple delete.  This involves just clearing an old matching leaf.  The
Josh Boyer 9cb73b
+     metadata blocks don't change otherwise.  The old leaf won't be freed until
Josh Boyer 9cb73b
+     after the RCU grace period.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (3) Insertion replacing part of a subtree that we haven't yet entered.  This
Josh Boyer 9cb73b
+     may involve replacement of part of that subtree - but that won't affect
Josh Boyer 9cb73b
+     the iteration as we won't have reached the pointer to it yet and the
Josh Boyer 9cb73b
+     ancestry blocks are not replaced (the layout of those does not change).
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (4) Insertion replacing nodes that we're actively processing.  This isn't a
Josh Boyer 9cb73b
+     problem as we've passed the anchoring pointer and won't switch onto the
Josh Boyer 9cb73b
+     new layout until we follow the back pointers - at which point we've
Josh Boyer 9cb73b
+     already examined the leaves in the replaced node (we iterate over all the
Josh Boyer 9cb73b
+     leaves in a node before following any of its metadata pointers).
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     We might, however, re-see some leaves that have been split out into a new
Josh Boyer 9cb73b
+     branch that's in a slot further along than we were at.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (5) Insertion replacing nodes that we're processing a dependent branch of.
Josh Boyer 9cb73b
+     This won't affect us until we follow the back pointers.  Similar to (4).
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (6) Deletion collapsing a branch under us.  This doesn't affect us because the
Josh Boyer 9cb73b
+     back pointers will get us back to the parent of the new node before we
Josh Boyer 9cb73b
+     could see the new node.  The entire collapsed subtree is thrown away
Josh Boyer 9cb73b
+     unchanged - and will still be rooted on the same slot, so we shouldn't
Josh Boyer 9cb73b
+     process it a second time as we'll go back to slot + 1.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+Note:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+ (*) Under some circumstances, we need to simultaneously change the parent
Josh Boyer 9cb73b
+     pointer and the parent slot pointer on a node (say, for example, we
Josh Boyer 9cb73b
+     inserted another node before it and moved it up a level).  We cannot do
Josh Boyer 9cb73b
+     this without locking against a read - so we have to replace that node too.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+     However, when we're changing a shortcut into a node this isn't a problem
Josh Boyer 9cb73b
+     as shortcuts only have one slot and so the parent slot number isn't used
Josh Boyer 9cb73b
+     when traversing backwards over one.  This means that it's okay to change
Josh Boyer 9cb73b
+     the slot number first - provided suitable barriers are used to make sure
Josh Boyer 9cb73b
+     the parent slot number is read after the back pointer.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+Obsolete blocks and leaves are freed up after an RCU grace period has passed,
Josh Boyer 9cb73b
+so as long as anyone doing walking or iteration holds the RCU read lock, the
Josh Boyer 9cb73b
+old superstructure should not go away on them.
Josh Boyer 9cb73b
diff --git a/include/linux/assoc_array.h b/include/linux/assoc_array.h
Josh Boyer 9cb73b
new file mode 100644
Josh Boyer 9cb73b
index 0000000..9a193b8
Josh Boyer 9cb73b
--- /dev/null
Josh Boyer 9cb73b
+++ b/include/linux/assoc_array.h
Josh Boyer 9cb73b
@@ -0,0 +1,92 @@
Josh Boyer 9cb73b
+/* Generic associative array implementation.
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * See Documentation/assoc_array.txt for information.
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
Josh Boyer 9cb73b
+ * Written by David Howells (dhowells@redhat.com)
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * This program is free software; you can redistribute it and/or
Josh Boyer 9cb73b
+ * modify it under the terms of the GNU General Public Licence
Josh Boyer 9cb73b
+ * as published by the Free Software Foundation; either version
Josh Boyer 9cb73b
+ * 2 of the Licence, or (at your option) any later version.
Josh Boyer 9cb73b
+ */
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+#ifndef _LINUX_ASSOC_ARRAY_H
Josh Boyer 9cb73b
+#define _LINUX_ASSOC_ARRAY_H
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+#ifdef CONFIG_ASSOCIATIVE_ARRAY
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+#include <linux/types.h>
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+#define ASSOC_ARRAY_KEY_CHUNK_SIZE BITS_PER_LONG /* Key data retrieved in chunks of this size */
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+/*
Josh Boyer 9cb73b
+ * Generic associative array.
Josh Boyer 9cb73b
+ */
Josh Boyer 9cb73b
+struct assoc_array {
Josh Boyer 9cb73b
+	struct assoc_array_ptr	*root;		/* The node at the root of the tree */
Josh Boyer 9cb73b
+	unsigned long		nr_leaves_on_tree;
Josh Boyer 9cb73b
+};
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+/*
Josh Boyer 9cb73b
+ * Operations on objects and index keys for use by array manipulation routines.
Josh Boyer 9cb73b
+ */
Josh Boyer 9cb73b
+struct assoc_array_ops {
Josh Boyer 9cb73b
+	/* Method to get a chunk of an index key from caller-supplied data */
Josh Boyer 9cb73b
+	unsigned long (*get_key_chunk)(const void *index_key, int level);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	/* Method to get a piece of an object's index key */
Josh Boyer 9cb73b
+	unsigned long (*get_object_key_chunk)(const void *object, int level);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	/* Is this the object we're looking for? */
Josh Boyer 9cb73b
+	bool (*compare_object)(const void *object, const void *index_key);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	/* How different are two objects, to a bit position in their keys? (or
Josh Boyer 9cb73b
+	 * -1 if they're the same)
Josh Boyer 9cb73b
+	 */
Josh Boyer 9cb73b
+	int (*diff_objects)(const void *a, const void *b);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	/* Method to free an object. */
Josh Boyer 9cb73b
+	void (*free_object)(void *object);
Josh Boyer 9cb73b
+};
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+/*
Josh Boyer 9cb73b
+ * Access and manipulation functions.
Josh Boyer 9cb73b
+ */
Josh Boyer 9cb73b
+struct assoc_array_edit;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+static inline void assoc_array_init(struct assoc_array *array)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	array->root = NULL;
Josh Boyer 9cb73b
+	array->nr_leaves_on_tree = 0;
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+extern int assoc_array_iterate(const struct assoc_array *array,
Josh Boyer 9cb73b
+			       int (*iterator)(const void *object,
Josh Boyer 9cb73b
+					       void *iterator_data),
Josh Boyer 9cb73b
+			       void *iterator_data);
Josh Boyer 9cb73b
+extern void *assoc_array_find(const struct assoc_array *array,
Josh Boyer 9cb73b
+			      const struct assoc_array_ops *ops,
Josh Boyer 9cb73b
+			      const void *index_key);
Josh Boyer 9cb73b
+extern void assoc_array_destroy(struct assoc_array *array,
Josh Boyer 9cb73b
+				const struct assoc_array_ops *ops);
Josh Boyer 9cb73b
+extern struct assoc_array_edit *assoc_array_insert(struct assoc_array *array,
Josh Boyer 9cb73b
+						   const struct assoc_array_ops *ops,
Josh Boyer 9cb73b
+						   const void *index_key,
Josh Boyer 9cb73b
+						   void *object);
Josh Boyer 9cb73b
+extern void assoc_array_insert_set_object(struct assoc_array_edit *edit,
Josh Boyer 9cb73b
+					  void *object);
Josh Boyer 9cb73b
+extern struct assoc_array_edit *assoc_array_delete(struct assoc_array *array,
Josh Boyer 9cb73b
+						   const struct assoc_array_ops *ops,
Josh Boyer 9cb73b
+						   const void *index_key);
Josh Boyer 9cb73b
+extern struct assoc_array_edit *assoc_array_clear(struct assoc_array *array,
Josh Boyer 9cb73b
+						  const struct assoc_array_ops *ops);
Josh Boyer 9cb73b
+extern void assoc_array_apply_edit(struct assoc_array_edit *edit);
Josh Boyer 9cb73b
+extern void assoc_array_cancel_edit(struct assoc_array_edit *edit);
Josh Boyer 9cb73b
+extern int assoc_array_gc(struct assoc_array *array,
Josh Boyer 9cb73b
+			  const struct assoc_array_ops *ops,
Josh Boyer 9cb73b
+			  bool (*iterator)(void *object, void *iterator_data),
Josh Boyer 9cb73b
+			  void *iterator_data);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+#endif /* CONFIG_ASSOCIATIVE_ARRAY */
Josh Boyer 9cb73b
+#endif /* _LINUX_ASSOC_ARRAY_H */
Josh Boyer 9cb73b
diff --git a/include/linux/assoc_array_priv.h b/include/linux/assoc_array_priv.h
Josh Boyer 9cb73b
new file mode 100644
Josh Boyer 9cb73b
index 0000000..711275e
Josh Boyer 9cb73b
--- /dev/null
Josh Boyer 9cb73b
+++ b/include/linux/assoc_array_priv.h
Josh Boyer 9cb73b
@@ -0,0 +1,182 @@
Josh Boyer 9cb73b
+/* Private definitions for the generic associative array implementation.
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * See Documentation/assoc_array.txt for information.
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
Josh Boyer 9cb73b
+ * Written by David Howells (dhowells@redhat.com)
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * This program is free software; you can redistribute it and/or
Josh Boyer 9cb73b
+ * modify it under the terms of the GNU General Public Licence
Josh Boyer 9cb73b
+ * as published by the Free Software Foundation; either version
Josh Boyer 9cb73b
+ * 2 of the Licence, or (at your option) any later version.
Josh Boyer 9cb73b
+ */
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+#ifndef _LINUX_ASSOC_ARRAY_PRIV_H
Josh Boyer 9cb73b
+#define _LINUX_ASSOC_ARRAY_PRIV_H
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+#ifdef CONFIG_ASSOCIATIVE_ARRAY
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+#include <linux/assoc_array.h>
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+#define ASSOC_ARRAY_FAN_OUT		16	/* Number of slots per node */
Josh Boyer 9cb73b
+#define ASSOC_ARRAY_FAN_MASK		(ASSOC_ARRAY_FAN_OUT - 1)
Josh Boyer 9cb73b
+#define ASSOC_ARRAY_LEVEL_STEP		(ilog2(ASSOC_ARRAY_FAN_OUT))
Josh Boyer 9cb73b
+#define ASSOC_ARRAY_LEVEL_STEP_MASK	(ASSOC_ARRAY_LEVEL_STEP - 1)
Josh Boyer 9cb73b
+#define ASSOC_ARRAY_KEY_CHUNK_MASK	(ASSOC_ARRAY_KEY_CHUNK_SIZE - 1)
Josh Boyer 9cb73b
+#define ASSOC_ARRAY_KEY_CHUNK_SHIFT	(ilog2(BITS_PER_LONG))
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+/*
Josh Boyer 9cb73b
+ * Undefined type representing a pointer with type information in the bottom
Josh Boyer 9cb73b
+ * two bits.
Josh Boyer 9cb73b
+ */
Josh Boyer 9cb73b
+struct assoc_array_ptr;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+/*
Josh Boyer 9cb73b
+ * An N-way node in the tree.
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * Each slot contains one of four things:
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ *	(1) Nothing (NULL).
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ *	(2) A leaf object (pointer types 0).
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ *	(3) A next-level node (pointer type 1, subtype 0).
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ *	(4) A shortcut (pointer type 1, subtype 1).
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * The tree is optimised for search-by-ID, but permits reasonable iteration
Josh Boyer 9cb73b
+ * also.
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * The tree is navigated by constructing an index key consisting of an array of
Josh Boyer 9cb73b
+ * segments, where each segment is ilog2(ASSOC_ARRAY_FAN_OUT) bits in size.
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * The segments correspond to levels of the tree (the first segment is used at
Josh Boyer 9cb73b
+ * level 0, the second at level 1, etc.).
Josh Boyer 9cb73b
+ */
Josh Boyer 9cb73b
+struct assoc_array_node {
Josh Boyer 9cb73b
+	struct assoc_array_ptr	*back_pointer;
Josh Boyer 9cb73b
+	u8			parent_slot;
Josh Boyer 9cb73b
+	struct assoc_array_ptr	*slots[ASSOC_ARRAY_FAN_OUT];
Josh Boyer 9cb73b
+	unsigned long		nr_leaves_on_branch;
Josh Boyer 9cb73b
+};
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+/*
Josh Boyer 9cb73b
+ * A shortcut through the index space out to where a collection of nodes/leaves
Josh Boyer 9cb73b
+ * with the same IDs live.
Josh Boyer 9cb73b
+ */
Josh Boyer 9cb73b
+struct assoc_array_shortcut {
Josh Boyer 9cb73b
+	struct assoc_array_ptr	*back_pointer;
Josh Boyer 9cb73b
+	int			parent_slot;
Josh Boyer 9cb73b
+	int			skip_to_level;
Josh Boyer 9cb73b
+	struct assoc_array_ptr	*next_node;
Josh Boyer 9cb73b
+	unsigned long		index_key[];
Josh Boyer 9cb73b
+};
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+/*
Josh Boyer 9cb73b
+ * Preallocation cache.
Josh Boyer 9cb73b
+ */
Josh Boyer 9cb73b
+struct assoc_array_edit {
Josh Boyer 9cb73b
+	struct rcu_head			rcu;
Josh Boyer 9cb73b
+	struct assoc_array		*array;
Josh Boyer 9cb73b
+	const struct assoc_array_ops	*ops;
Josh Boyer 9cb73b
+	const struct assoc_array_ops	*ops_for_excised_subtree;
Josh Boyer 9cb73b
+	struct assoc_array_ptr		*leaf;
Josh Boyer 9cb73b
+	struct assoc_array_ptr		**leaf_p;
Josh Boyer 9cb73b
+	struct assoc_array_ptr		*dead_leaf;
Josh Boyer 9cb73b
+	struct assoc_array_ptr		*new_meta[3];
Josh Boyer 9cb73b
+	struct assoc_array_ptr		*excised_meta[1];
Josh Boyer 9cb73b
+	struct assoc_array_ptr		*excised_subtree;
Josh Boyer 9cb73b
+	struct assoc_array_ptr		**set_backpointers[ASSOC_ARRAY_FAN_OUT];
Josh Boyer 9cb73b
+	struct assoc_array_ptr		*set_backpointers_to;
Josh Boyer 9cb73b
+	struct assoc_array_node		*adjust_count_on;
Josh Boyer 9cb73b
+	long				adjust_count_by;
Josh Boyer 9cb73b
+	struct {
Josh Boyer 9cb73b
+		struct assoc_array_ptr	**ptr;
Josh Boyer 9cb73b
+		struct assoc_array_ptr	*to;
Josh Boyer 9cb73b
+	} set[2];
Josh Boyer 9cb73b
+	struct {
Josh Boyer 9cb73b
+		u8			*p;
Josh Boyer 9cb73b
+		u8			to;
Josh Boyer 9cb73b
+	} set_parent_slot[1];
Josh Boyer 9cb73b
+	u8				segment_cache[ASSOC_ARRAY_FAN_OUT + 1];
Josh Boyer 9cb73b
+};
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+/*
Josh Boyer 9cb73b
+ * Internal tree member pointers are marked in the bottom one or two bits to
Josh Boyer 9cb73b
+ * indicate what type they are so that we don't have to look behind every
Josh Boyer 9cb73b
+ * pointer to see what it points to.
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * We provide functions to test type annotations and to create and translate
Josh Boyer 9cb73b
+ * the annotated pointers.
Josh Boyer 9cb73b
+ */
Josh Boyer 9cb73b
+#define ASSOC_ARRAY_PTR_TYPE_MASK 0x1UL
Josh Boyer 9cb73b
+#define ASSOC_ARRAY_PTR_LEAF_TYPE 0x0UL	/* Points to leaf (or nowhere) */
Josh Boyer 9cb73b
+#define ASSOC_ARRAY_PTR_META_TYPE 0x1UL	/* Points to node or shortcut */
Josh Boyer 9cb73b
+#define ASSOC_ARRAY_PTR_SUBTYPE_MASK	0x2UL
Josh Boyer 9cb73b
+#define ASSOC_ARRAY_PTR_NODE_SUBTYPE	0x0UL
Josh Boyer 9cb73b
+#define ASSOC_ARRAY_PTR_SHORTCUT_SUBTYPE 0x2UL
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+static inline bool assoc_array_ptr_is_meta(const struct assoc_array_ptr *x)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	return (unsigned long)x & ASSOC_ARRAY_PTR_TYPE_MASK;
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+static inline bool assoc_array_ptr_is_leaf(const struct assoc_array_ptr *x)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	return !assoc_array_ptr_is_meta(x);
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+static inline bool assoc_array_ptr_is_shortcut(const struct assoc_array_ptr *x)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	return (unsigned long)x & ASSOC_ARRAY_PTR_SUBTYPE_MASK;
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+static inline bool assoc_array_ptr_is_node(const struct assoc_array_ptr *x)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	return !assoc_array_ptr_is_shortcut(x);
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+static inline void *assoc_array_ptr_to_leaf(const struct assoc_array_ptr *x)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	return (void *)((unsigned long)x & ~ASSOC_ARRAY_PTR_TYPE_MASK);
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+static inline
Josh Boyer 9cb73b
+unsigned long __assoc_array_ptr_to_meta(const struct assoc_array_ptr *x)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	return (unsigned long)x &
Josh Boyer 9cb73b
+		~(ASSOC_ARRAY_PTR_SUBTYPE_MASK | ASSOC_ARRAY_PTR_TYPE_MASK);
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+static inline
Josh Boyer 9cb73b
+struct assoc_array_node *assoc_array_ptr_to_node(const struct assoc_array_ptr *x)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	return (struct assoc_array_node *)__assoc_array_ptr_to_meta(x);
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+static inline
Josh Boyer 9cb73b
+struct assoc_array_shortcut *assoc_array_ptr_to_shortcut(const struct assoc_array_ptr *x)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	return (struct assoc_array_shortcut *)__assoc_array_ptr_to_meta(x);
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+static inline
Josh Boyer 9cb73b
+struct assoc_array_ptr *__assoc_array_x_to_ptr(const void *p, unsigned long t)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	return (struct assoc_array_ptr *)((unsigned long)p | t);
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+static inline
Josh Boyer 9cb73b
+struct assoc_array_ptr *assoc_array_leaf_to_ptr(const void *p)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	return __assoc_array_x_to_ptr(p, ASSOC_ARRAY_PTR_LEAF_TYPE);
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+static inline
Josh Boyer 9cb73b
+struct assoc_array_ptr *assoc_array_node_to_ptr(const struct assoc_array_node *p)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	return __assoc_array_x_to_ptr(
Josh Boyer 9cb73b
+		p, ASSOC_ARRAY_PTR_META_TYPE | ASSOC_ARRAY_PTR_NODE_SUBTYPE);
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+static inline
Josh Boyer 9cb73b
+struct assoc_array_ptr *assoc_array_shortcut_to_ptr(const struct assoc_array_shortcut *p)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	return __assoc_array_x_to_ptr(
Josh Boyer 9cb73b
+		p, ASSOC_ARRAY_PTR_META_TYPE | ASSOC_ARRAY_PTR_SHORTCUT_SUBTYPE);
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+#endif /* CONFIG_ASSOCIATIVE_ARRAY */
Josh Boyer 9cb73b
+#endif /* _LINUX_ASSOC_ARRAY_PRIV_H */
Josh Boyer 9cb73b
diff --git a/lib/Kconfig b/lib/Kconfig
Josh Boyer 9cb73b
index 35da513..b7d3234 100644
Josh Boyer 9cb73b
--- a/lib/Kconfig
Josh Boyer 9cb73b
+++ b/lib/Kconfig
Josh Boyer 9cb73b
@@ -312,6 +312,20 @@ config TEXTSEARCH_FSM
Josh Boyer 9cb73b
 config BTREE
Josh Boyer 9cb73b
 	boolean
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
+config ASSOCIATIVE_ARRAY
Josh Boyer 9cb73b
+	bool
Josh Boyer 9cb73b
+	help
Josh Boyer 9cb73b
+	  Generic associative array.  Can be searched and iterated over whilst
Josh Boyer 9cb73b
+	  it is being modified.  It is also reasonably quick to search and
Josh Boyer 9cb73b
+	  modify.  The algorithms are non-recursive, and the trees are highly
Josh Boyer 9cb73b
+	  capacious.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	  See:
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+		Documentation/assoc_array.txt
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	  for more information.
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
 config HAS_IOMEM
Josh Boyer 9cb73b
 	boolean
Josh Boyer 9cb73b
 	depends on !NO_IOMEM
Josh Boyer 9cb73b
diff --git a/lib/Makefile b/lib/Makefile
Josh Boyer 9cb73b
index 7baccfd..2c59891 100644
Josh Boyer 9cb73b
--- a/lib/Makefile
Josh Boyer 9cb73b
+++ b/lib/Makefile
Josh Boyer 9cb73b
@@ -49,6 +49,7 @@ CFLAGS_hweight.o = $(subst $(quote),,$(CONFIG_ARCH_HWEIGHT_CFLAGS))
Josh Boyer 9cb73b
 obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
Josh Boyer 9cb73b
 
Josh Boyer 9cb73b
 obj-$(CONFIG_BTREE) += btree.o
Josh Boyer 9cb73b
+obj-$(CONFIG_ASSOCIATIVE_ARRAY) += assoc_array.o
Josh Boyer 9cb73b
 obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o
Josh Boyer 9cb73b
 obj-$(CONFIG_DEBUG_LIST) += list_debug.o
Josh Boyer 9cb73b
 obj-$(CONFIG_DEBUG_OBJECTS) += debugobjects.o
Josh Boyer 9cb73b
diff --git a/lib/assoc_array.c b/lib/assoc_array.c
Josh Boyer 9cb73b
new file mode 100644
Josh Boyer 9cb73b
index 0000000..a095281
Josh Boyer 9cb73b
--- /dev/null
Josh Boyer 9cb73b
+++ b/lib/assoc_array.c
Josh Boyer 9cb73b
@@ -0,0 +1,1745 @@
Josh Boyer 9cb73b
+/* Generic associative array implementation.
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * See Documentation/assoc_array.txt for information.
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
Josh Boyer 9cb73b
+ * Written by David Howells (dhowells@redhat.com)
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * This program is free software; you can redistribute it and/or
Josh Boyer 9cb73b
+ * modify it under the terms of the GNU General Public Licence
Josh Boyer 9cb73b
+ * as published by the Free Software Foundation; either version
Josh Boyer 9cb73b
+ * 2 of the Licence, or (at your option) any later version.
Josh Boyer 9cb73b
+ */
Josh Boyer 9cb73b
+//#define DEBUG
Josh Boyer 9cb73b
+#include <linux/slab.h>
Josh Boyer 9cb73b
+#include <linux/assoc_array_priv.h>
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+/*
Josh Boyer 9cb73b
+ * Iterate over an associative array.  The caller must hold the RCU read lock
Josh Boyer 9cb73b
+ * or better.
Josh Boyer 9cb73b
+ */
Josh Boyer 9cb73b
+static int assoc_array_subtree_iterate(const struct assoc_array_ptr *root,
Josh Boyer 9cb73b
+				       const struct assoc_array_ptr *stop,
Josh Boyer 9cb73b
+				       int (*iterator)(const void *leaf,
Josh Boyer 9cb73b
+						       void *iterator_data),
Josh Boyer 9cb73b
+				       void *iterator_data)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	const struct assoc_array_shortcut *shortcut;
Josh Boyer 9cb73b
+	const struct assoc_array_node *node;
Josh Boyer 9cb73b
+	const struct assoc_array_ptr *cursor, *ptr, *parent;
Josh Boyer 9cb73b
+	unsigned long has_meta;
Josh Boyer 9cb73b
+	int slot, ret;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	cursor = root;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+begin_node:
Josh Boyer 9cb73b
+	if (assoc_array_ptr_is_shortcut(cursor)) {
Josh Boyer 9cb73b
+		/* Descend through a shortcut */
Josh Boyer 9cb73b
+		shortcut = assoc_array_ptr_to_shortcut(cursor);
Josh Boyer 9cb73b
+		smp_read_barrier_depends();
Josh Boyer 9cb73b
+		cursor = ACCESS_ONCE(shortcut->next_node);
Josh Boyer 9cb73b
+	}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	node = assoc_array_ptr_to_node(cursor);
Josh Boyer 9cb73b
+	smp_read_barrier_depends();
Josh Boyer 9cb73b
+	slot = 0;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	/* We perform two passes of each node.
Josh Boyer 9cb73b
+	 *
Josh Boyer 9cb73b
+	 * The first pass does all the leaves in this node.  This means we
Josh Boyer 9cb73b
+	 * don't miss any leaves if the node is split up by insertion whilst
Josh Boyer 9cb73b
+	 * we're iterating over the branches rooted here (we may, however, see
Josh Boyer 9cb73b
+	 * some leaves twice).
Josh Boyer 9cb73b
+	 */
Josh Boyer 9cb73b
+	has_meta = 0;
Josh Boyer 9cb73b
+	for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
Josh Boyer 9cb73b
+		ptr = ACCESS_ONCE(node->slots[slot]);
Josh Boyer 9cb73b
+		has_meta |= (unsigned long)ptr;
Josh Boyer 9cb73b
+		if (ptr && assoc_array_ptr_is_leaf(ptr)) {
Josh Boyer 9cb73b
+			/* We need a barrier between the read of the pointer
Josh Boyer 9cb73b
+			 * and dereferencing the pointer - but only if we are
Josh Boyer 9cb73b
+			 * actually going to dereference it.
Josh Boyer 9cb73b
+			 */
Josh Boyer 9cb73b
+			smp_read_barrier_depends();
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+			/* Invoke the callback */
Josh Boyer 9cb73b
+			ret = iterator(assoc_array_ptr_to_leaf(ptr),
Josh Boyer 9cb73b
+				       iterator_data);
Josh Boyer 9cb73b
+			if (ret)
Josh Boyer 9cb73b
+				return ret;
Josh Boyer 9cb73b
+		}
Josh Boyer 9cb73b
+	}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	/* The second pass attends to all the metadata pointers.  If we follow
Josh Boyer 9cb73b
+	 * one of these we may find that we don't come back here, but rather go
Josh Boyer 9cb73b
+	 * back to a replacement node with the leaves in a different layout.
Josh Boyer 9cb73b
+	 *
Josh Boyer 9cb73b
+	 * We are guaranteed to make progress, however, as the slot number for
Josh Boyer 9cb73b
+	 * a particular portion of the key space cannot change - and we
Josh Boyer 9cb73b
+	 * continue at the back pointer + 1.
Josh Boyer 9cb73b
+	 */
Josh Boyer 9cb73b
+	if (!(has_meta & ASSOC_ARRAY_PTR_META_TYPE))
Josh Boyer 9cb73b
+		goto finished_node;
Josh Boyer 9cb73b
+	slot = 0;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+continue_node:
Josh Boyer 9cb73b
+	node = assoc_array_ptr_to_node(cursor);
Josh Boyer 9cb73b
+	smp_read_barrier_depends();
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
Josh Boyer 9cb73b
+		ptr = ACCESS_ONCE(node->slots[slot]);
Josh Boyer 9cb73b
+		if (assoc_array_ptr_is_meta(ptr)) {
Josh Boyer 9cb73b
+			cursor = ptr;
Josh Boyer 9cb73b
+			goto begin_node;
Josh Boyer 9cb73b
+		}
Josh Boyer 9cb73b
+	}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+finished_node:
Josh Boyer 9cb73b
+	/* Move up to the parent (may need to skip back over a shortcut) */
Josh Boyer 9cb73b
+	parent = ACCESS_ONCE(node->back_pointer);
Josh Boyer 9cb73b
+	slot = node->parent_slot;
Josh Boyer 9cb73b
+	if (parent == stop)
Josh Boyer 9cb73b
+		return 0;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	if (assoc_array_ptr_is_shortcut(parent)) {
Josh Boyer 9cb73b
+		shortcut = assoc_array_ptr_to_shortcut(parent);
Josh Boyer 9cb73b
+		smp_read_barrier_depends();
Josh Boyer 9cb73b
+		cursor = parent;
Josh Boyer 9cb73b
+		parent = ACCESS_ONCE(shortcut->back_pointer);
Josh Boyer 9cb73b
+		slot = shortcut->parent_slot;
Josh Boyer 9cb73b
+		if (parent == stop)
Josh Boyer 9cb73b
+			return 0;
Josh Boyer 9cb73b
+	}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	/* Ascend to next slot in parent node */
Josh Boyer 9cb73b
+	cursor = parent;
Josh Boyer 9cb73b
+	slot++;
Josh Boyer 9cb73b
+	goto continue_node;
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+/**
Josh Boyer 9cb73b
+ * assoc_array_iterate - Pass all objects in the array to a callback
Josh Boyer 9cb73b
+ * @array: The array to iterate over.
Josh Boyer 9cb73b
+ * @iterator: The callback function.
Josh Boyer 9cb73b
+ * @iterator_data: Private data for the callback function.
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * Iterate over all the objects in an associative array.  Each one will be
Josh Boyer 9cb73b
+ * presented to the iterator function.
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * If the array is being modified concurrently with the iteration then it is
Josh Boyer 9cb73b
+ * possible that some objects in the array will be passed to the iterator
Josh Boyer 9cb73b
+ * callback more than once - though every object should be passed at least
Josh Boyer 9cb73b
+ * once.  If this is undesirable then the caller must lock against modification
Josh Boyer 9cb73b
+ * for the duration of this function.
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * The function will return 0 if no objects were in the array or else it will
Josh Boyer 9cb73b
+ * return the result of the last iterator function called.  Iteration stops
Josh Boyer 9cb73b
+ * immediately if any call to the iteration function results in a non-zero
Josh Boyer 9cb73b
+ * return.
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * The caller should hold the RCU read lock or better if concurrent
Josh Boyer 9cb73b
+ * modification is possible.
Josh Boyer 9cb73b
+ */
Josh Boyer 9cb73b
+int assoc_array_iterate(const struct assoc_array *array,
Josh Boyer 9cb73b
+			int (*iterator)(const void *object,
Josh Boyer 9cb73b
+					void *iterator_data),
Josh Boyer 9cb73b
+			void *iterator_data)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	struct assoc_array_ptr *root = ACCESS_ONCE(array->root);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	if (!root)
Josh Boyer 9cb73b
+		return 0;
Josh Boyer 9cb73b
+	return assoc_array_subtree_iterate(root, NULL, iterator, iterator_data);
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+enum assoc_array_walk_status {
Josh Boyer 9cb73b
+	assoc_array_walk_tree_empty,
Josh Boyer 9cb73b
+	assoc_array_walk_found_terminal_node,
Josh Boyer 9cb73b
+	assoc_array_walk_found_wrong_shortcut,
Josh Boyer 9cb73b
+} status;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+struct assoc_array_walk_result {
Josh Boyer 9cb73b
+	struct {
Josh Boyer 9cb73b
+		struct assoc_array_node	*node;	/* Node in which leaf might be found */
Josh Boyer 9cb73b
+		int		level;
Josh Boyer 9cb73b
+		int		slot;
Josh Boyer 9cb73b
+	} terminal_node;
Josh Boyer 9cb73b
+	struct {
Josh Boyer 9cb73b
+		struct assoc_array_shortcut *shortcut;
Josh Boyer 9cb73b
+		int		level;
Josh Boyer 9cb73b
+		int		sc_level;
Josh Boyer 9cb73b
+		unsigned long	sc_segments;
Josh Boyer 9cb73b
+		unsigned long	dissimilarity;
Josh Boyer 9cb73b
+	} wrong_shortcut;
Josh Boyer 9cb73b
+};
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+/*
Josh Boyer 9cb73b
+ * Navigate through the internal tree looking for the closest node to the key.
Josh Boyer 9cb73b
+ */
Josh Boyer 9cb73b
+static enum assoc_array_walk_status
Josh Boyer 9cb73b
+assoc_array_walk(const struct assoc_array *array,
Josh Boyer 9cb73b
+		 const struct assoc_array_ops *ops,
Josh Boyer 9cb73b
+		 const void *index_key,
Josh Boyer 9cb73b
+		 struct assoc_array_walk_result *result)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	struct assoc_array_shortcut *shortcut;
Josh Boyer 9cb73b
+	struct assoc_array_node *node;
Josh Boyer 9cb73b
+	struct assoc_array_ptr *cursor, *ptr;
Josh Boyer 9cb73b
+	unsigned long sc_segments, dissimilarity;
Josh Boyer 9cb73b
+	unsigned long segments;
Josh Boyer 9cb73b
+	int level, sc_level, next_sc_level;
Josh Boyer 9cb73b
+	int slot;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	pr_devel("-->%s()\n", __func__);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	cursor = ACCESS_ONCE(array->root);
Josh Boyer 9cb73b
+	if (!cursor)
Josh Boyer 9cb73b
+		return assoc_array_walk_tree_empty;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	level = 0;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	/* Use segments from the key for the new leaf to navigate through the
Josh Boyer 9cb73b
+	 * internal tree, skipping through nodes and shortcuts that are on
Josh Boyer 9cb73b
+	 * route to the destination.  Eventually we'll come to a slot that is
Josh Boyer 9cb73b
+	 * either empty or contains a leaf at which point we've found a node in
Josh Boyer 9cb73b
+	 * which the leaf we're looking for might be found or into which it
Josh Boyer 9cb73b
+	 * should be inserted.
Josh Boyer 9cb73b
+	 */
Josh Boyer 9cb73b
+jumped:
Josh Boyer 9cb73b
+	segments = ops->get_key_chunk(index_key, level);
Josh Boyer 9cb73b
+	pr_devel("segments[%d]: %lx\n", level, segments);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	if (assoc_array_ptr_is_shortcut(cursor))
Josh Boyer 9cb73b
+		goto follow_shortcut;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+consider_node:
Josh Boyer 9cb73b
+	node = assoc_array_ptr_to_node(cursor);
Josh Boyer 9cb73b
+	smp_read_barrier_depends();
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	slot = segments >> (level & ASSOC_ARRAY_KEY_CHUNK_MASK);
Josh Boyer 9cb73b
+	slot &= ASSOC_ARRAY_FAN_MASK;
Josh Boyer 9cb73b
+	ptr = ACCESS_ONCE(node->slots[slot]);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	pr_devel("consider slot %x [ix=%d type=%lu]\n",
Josh Boyer 9cb73b
+		 slot, level, (unsigned long)ptr & 3);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	if (!assoc_array_ptr_is_meta(ptr)) {
Josh Boyer 9cb73b
+		/* The node doesn't have a node/shortcut pointer in the slot
Josh Boyer 9cb73b
+		 * corresponding to the index key that we have to follow.
Josh Boyer 9cb73b
+		 */
Josh Boyer 9cb73b
+		result->terminal_node.node = node;
Josh Boyer 9cb73b
+		result->terminal_node.level = level;
Josh Boyer 9cb73b
+		result->terminal_node.slot = slot;
Josh Boyer 9cb73b
+		pr_devel("<--%s() = terminal_node\n", __func__);
Josh Boyer 9cb73b
+		return assoc_array_walk_found_terminal_node;
Josh Boyer 9cb73b
+	}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	if (assoc_array_ptr_is_node(ptr)) {
Josh Boyer 9cb73b
+		/* There is a pointer to a node in the slot corresponding to
Josh Boyer 9cb73b
+		 * this index key segment, so we need to follow it.
Josh Boyer 9cb73b
+		 */
Josh Boyer 9cb73b
+		cursor = ptr;
Josh Boyer 9cb73b
+		level += ASSOC_ARRAY_LEVEL_STEP;
Josh Boyer 9cb73b
+		if ((level & ASSOC_ARRAY_KEY_CHUNK_MASK) != 0)
Josh Boyer 9cb73b
+			goto consider_node;
Josh Boyer 9cb73b
+		goto jumped;
Josh Boyer 9cb73b
+	}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	/* There is a shortcut in the slot corresponding to the index key
Josh Boyer 9cb73b
+	 * segment.  We follow the shortcut if its partial index key matches
Josh Boyer 9cb73b
+	 * this leaf's.  Otherwise we need to split the shortcut.
Josh Boyer 9cb73b
+	 */
Josh Boyer 9cb73b
+	cursor = ptr;
Josh Boyer 9cb73b
+follow_shortcut:
Josh Boyer 9cb73b
+	shortcut = assoc_array_ptr_to_shortcut(cursor);
Josh Boyer 9cb73b
+	smp_read_barrier_depends();
Josh Boyer 9cb73b
+	pr_devel("shortcut to %d\n", shortcut->skip_to_level);
Josh Boyer 9cb73b
+	sc_level = level + ASSOC_ARRAY_LEVEL_STEP;
Josh Boyer 9cb73b
+	BUG_ON(sc_level > shortcut->skip_to_level);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	do {
Josh Boyer 9cb73b
+		/* Check the leaf against the shortcut's index key a word at a
Josh Boyer 9cb73b
+		 * time, trimming the final word (the shortcut stores the index
Josh Boyer 9cb73b
+		 * key completely from the root to the shortcut's target).
Josh Boyer 9cb73b
+		 */
Josh Boyer 9cb73b
+		if ((sc_level & ASSOC_ARRAY_KEY_CHUNK_MASK) == 0)
Josh Boyer 9cb73b
+			segments = ops->get_key_chunk(index_key, sc_level);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+		sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT];
Josh Boyer 9cb73b
+		dissimilarity = segments ^ sc_segments;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+		if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) {
Josh Boyer 9cb73b
+			/* Trim segments that are beyond the shortcut */
Josh Boyer 9cb73b
+			int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK;
Josh Boyer 9cb73b
+			dissimilarity &= ~(ULONG_MAX << shift);
Josh Boyer 9cb73b
+			next_sc_level = shortcut->skip_to_level;
Josh Boyer 9cb73b
+		} else {
Josh Boyer 9cb73b
+			next_sc_level = sc_level + ASSOC_ARRAY_KEY_CHUNK_SIZE;
Josh Boyer 9cb73b
+			next_sc_level = round_down(next_sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE);
Josh Boyer 9cb73b
+		}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+		if (dissimilarity != 0) {
Josh Boyer 9cb73b
+			/* This shortcut points elsewhere */
Josh Boyer 9cb73b
+			result->wrong_shortcut.shortcut = shortcut;
Josh Boyer 9cb73b
+			result->wrong_shortcut.level = level;
Josh Boyer 9cb73b
+			result->wrong_shortcut.sc_level = sc_level;
Josh Boyer 9cb73b
+			result->wrong_shortcut.sc_segments = sc_segments;
Josh Boyer 9cb73b
+			result->wrong_shortcut.dissimilarity = dissimilarity;
Josh Boyer 9cb73b
+			return assoc_array_walk_found_wrong_shortcut;
Josh Boyer 9cb73b
+		}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+		sc_level = next_sc_level;
Josh Boyer 9cb73b
+	} while (sc_level < shortcut->skip_to_level);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	/* The shortcut matches the leaf's index to this point. */
Josh Boyer 9cb73b
+	cursor = ACCESS_ONCE(shortcut->next_node);
Josh Boyer 9cb73b
+	if (((level ^ sc_level) & ~ASSOC_ARRAY_KEY_CHUNK_MASK) != 0) {
Josh Boyer 9cb73b
+		level = sc_level;
Josh Boyer 9cb73b
+		goto jumped;
Josh Boyer 9cb73b
+	} else {
Josh Boyer 9cb73b
+		level = sc_level;
Josh Boyer 9cb73b
+		goto consider_node;
Josh Boyer 9cb73b
+	}
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+/**
Josh Boyer 9cb73b
+ * assoc_array_find - Find an object by index key
Josh Boyer 9cb73b
+ * @array: The associative array to search.
Josh Boyer 9cb73b
+ * @ops: The operations to use.
Josh Boyer 9cb73b
+ * @index_key: The key to the object.
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * Find an object in an associative array by walking through the internal tree
Josh Boyer 9cb73b
+ * to the node that should contain the object and then searching the leaves
Josh Boyer 9cb73b
+ * there.  NULL is returned if the requested object was not found in the array.
Josh Boyer 9cb73b
+ *
Josh Boyer 9cb73b
+ * The caller must hold the RCU read lock or better.
Josh Boyer 9cb73b
+ */
Josh Boyer 9cb73b
+void *assoc_array_find(const struct assoc_array *array,
Josh Boyer 9cb73b
+		       const struct assoc_array_ops *ops,
Josh Boyer 9cb73b
+		       const void *index_key)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	struct assoc_array_walk_result result;
Josh Boyer 9cb73b
+	const struct assoc_array_node *node;
Josh Boyer 9cb73b
+	const struct assoc_array_ptr *ptr;
Josh Boyer 9cb73b
+	const void *leaf;
Josh Boyer 9cb73b
+	int slot;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	if (assoc_array_walk(array, ops, index_key, &result) !=
Josh Boyer 9cb73b
+	    assoc_array_walk_found_terminal_node)
Josh Boyer 9cb73b
+		return NULL;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	node = result.terminal_node.node;
Josh Boyer 9cb73b
+	smp_read_barrier_depends();
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	/* If the target key is available to us, it's has to be pointed to by
Josh Boyer 9cb73b
+	 * the terminal node.
Josh Boyer 9cb73b
+	 */
Josh Boyer 9cb73b
+	for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
Josh Boyer 9cb73b
+		ptr = ACCESS_ONCE(node->slots[slot]);
Josh Boyer 9cb73b
+		if (ptr && assoc_array_ptr_is_leaf(ptr)) {
Josh Boyer 9cb73b
+			/* We need a barrier between the read of the pointer
Josh Boyer 9cb73b
+			 * and dereferencing the pointer - but only if we are
Josh Boyer 9cb73b
+			 * actually going to dereference it.
Josh Boyer 9cb73b
+			 */
Josh Boyer 9cb73b
+			leaf = assoc_array_ptr_to_leaf(ptr);
Josh Boyer 9cb73b
+			smp_read_barrier_depends();
Josh Boyer 9cb73b
+			if (ops->compare_object(leaf, index_key))
Josh Boyer 9cb73b
+				return (void *)leaf;
Josh Boyer 9cb73b
+		}
Josh Boyer 9cb73b
+	}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	return NULL;
Josh Boyer 9cb73b
+}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+/*
Josh Boyer 9cb73b
+ * Destructively iterate over an associative array.  The caller must prevent
Josh Boyer 9cb73b
+ * other simultaneous accesses.
Josh Boyer 9cb73b
+ */
Josh Boyer 9cb73b
+static void assoc_array_destroy_subtree(struct assoc_array_ptr *root,
Josh Boyer 9cb73b
+					const struct assoc_array_ops *ops)
Josh Boyer 9cb73b
+{
Josh Boyer 9cb73b
+	struct assoc_array_shortcut *shortcut;
Josh Boyer 9cb73b
+	struct assoc_array_node *node;
Josh Boyer 9cb73b
+	struct assoc_array_ptr *cursor, *parent = NULL;
Josh Boyer 9cb73b
+	int slot = -1;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	pr_devel("-->%s()\n", __func__);
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	cursor = root;
Josh Boyer 9cb73b
+	if (!cursor) {
Josh Boyer 9cb73b
+		pr_devel("empty\n");
Josh Boyer 9cb73b
+		return;
Josh Boyer 9cb73b
+	}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+move_to_meta:
Josh Boyer 9cb73b
+	if (assoc_array_ptr_is_shortcut(cursor)) {
Josh Boyer 9cb73b
+		/* Descend through a shortcut */
Josh Boyer 9cb73b
+		pr_devel("[%d] shortcut\n", slot);
Josh Boyer 9cb73b
+		BUG_ON(!assoc_array_ptr_is_shortcut(cursor));
Josh Boyer 9cb73b
+		shortcut = assoc_array_ptr_to_shortcut(cursor);
Josh Boyer 9cb73b
+		BUG_ON(shortcut->back_pointer != parent);
Josh Boyer 9cb73b
+		BUG_ON(slot != -1 && shortcut->parent_slot != slot);
Josh Boyer 9cb73b
+		parent = cursor;
Josh Boyer 9cb73b
+		cursor = shortcut->next_node;
Josh Boyer 9cb73b
+		slot = -1;
Josh Boyer 9cb73b
+		BUG_ON(!assoc_array_ptr_is_node(cursor));
Josh Boyer 9cb73b
+	}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	pr_devel("[%d] node\n", slot);
Josh Boyer 9cb73b
+	node = assoc_array_ptr_to_node(cursor);
Josh Boyer 9cb73b
+	BUG_ON(node->back_pointer != parent);
Josh Boyer 9cb73b
+	BUG_ON(slot != -1 && node->parent_slot != slot);
Josh Boyer 9cb73b
+	slot = 0;
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+continue_node:
Josh Boyer 9cb73b
+	pr_devel("Node %p [back=%p]\n", node, node->back_pointer);
Josh Boyer 9cb73b
+	for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
Josh Boyer 9cb73b
+		struct assoc_array_ptr *ptr = node->slots[slot];
Josh Boyer 9cb73b
+		if (!ptr)
Josh Boyer 9cb73b
+			continue;
Josh Boyer 9cb73b
+		if (assoc_array_ptr_is_meta(ptr)) {
Josh Boyer 9cb73b
+			parent = cursor;
Josh Boyer 9cb73b
+			cursor = ptr;
Josh Boyer 9cb73b
+			goto move_to_meta;
Josh Boyer 9cb73b
+		}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+		if (ops) {
Josh Boyer 9cb73b
+			pr_devel("[%d] free leaf\n", slot);
Josh Boyer 9cb73b
+			ops->free_object(assoc_array_ptr_to_leaf(ptr));
Josh Boyer 9cb73b
+		}
Josh Boyer 9cb73b
+	}
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	parent = node->back_pointer;
Josh Boyer 9cb73b
+	slot = node->parent_slot;
Josh Boyer 9cb73b
+	pr_devel("free node\n");
Josh Boyer 9cb73b
+	kfree(node);
Josh Boyer 9cb73b
+	if (!parent)
Josh Boyer 9cb73b
+		return; /* Done */
Josh Boyer 9cb73b
+
Josh Boyer 9cb73b
+	/* Move back up to the parent (may need to free a shortcut on
Josh Boyer 9cb73b
+	 * the way up) */