diff --git a/include/linux/sunrpc/auth_gss.h b/include/linux/sunrpc/auth_gss.h
index f1cfd4c..4b20b53 100644
--- a/include/linux/sunrpc/auth_gss.h
+++ b/include/linux/sunrpc/auth_gss.h
@@ -79,6 +79,7 @@ struct gss_cl_ctx {
 struct gss_upcall_msg;
 struct gss_cred {
 	struct rpc_cred		gc_base;
+	key_serial_t		gc_serial;
 	enum rpc_gss_svc	gc_service;
 	struct gss_cl_ctx __rcu	*gc_ctx;
 	struct gss_upcall_msg	*gc_upcall;
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index ae42a83..10d6e53 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -161,13 +161,19 @@ gss_put_ctx(struct gss_cl_ctx *ctx)
  * and a new one is protected by the pipe->lock.
  */
 static void
-gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx)
+gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx, struct key *key)
 {
 	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
 
 	if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags))
 		return;
 	gss_get_ctx(ctx);
+	if (key != NULL)
+		gss_cred->gc_serial = key->serial;
+	else
+		pr_warn_ratelimited("RPC        no gss-ctx key for uid %d "
+				"gss_cred and context will not be destroyed "
+				" upon kdestroy\n", cred->cr_uid);
 	rcu_assign_pointer(gss_cred->gc_ctx, ctx);
 	set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
 	smp_mb__before_clear_bit();
@@ -302,6 +308,8 @@ err:
 struct gss_upcall_msg {
 	atomic_t count;
 	kuid_t	uid;
+	struct key *key;
+	char *cc_name;
 	struct rpc_pipe_msg msg;
 	struct list_head list;
 	struct gss_auth *auth;
@@ -347,7 +355,11 @@ gss_release_msg(struct gss_upcall_msg *gss_msg)
 	BUG_ON(!list_empty(&gss_msg->list));
 	if (gss_msg->ctx != NULL)
 		gss_put_ctx(gss_msg->ctx);
+	if (gss_msg->key != NULL)
+		/* balance key_get in keyring_search */
+		key_put(gss_msg->key);
 	rpc_destroy_wait_queue(&gss_msg->rpc_waitqueue);
+	kfree(gss_msg->cc_name);
 	kfree(gss_msg);
 }
 
@@ -417,7 +429,8 @@ gss_handle_downcall_result(struct gss_cred *gss_cred, struct gss_upcall_msg *gss
 		if (gss_msg->ctx == NULL)
 			break;
 		clear_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags);
-		gss_cred_set_ctx(&gss_cred->gc_base, gss_msg->ctx);
+		gss_cred_set_ctx(&gss_cred->gc_base, gss_msg->ctx,
+				gss_msg->key);
 		break;
 	case -EKEYEXPIRED:
 		set_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags);
@@ -427,6 +440,67 @@ gss_handle_downcall_result(struct gss_cred *gss_cred, struct gss_upcall_msg *gss
 	rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno);
 }
 
+
+#define GSS_CTX_ID_MAXLEN 20 /* "gss-ctx_" + NFS_UINT_MAXLEN  + 1 */
+
+static void
+gss_find_ctx_key(uid_t uid, struct gss_upcall_msg *msg)
+{
+	key_ref_t rkey;
+	struct key *key;
+	struct user_key_payload *payload;
+	char *id;
+	const struct cred *cred = get_current_cred();
+	int ret;
+
+	if (!cred->user->session_keyring)
+		goto out;
+
+	id = kzalloc(GSS_CTX_ID_MAXLEN, GFP_NOFS);
+	if (!id)
+		goto out;
+
+	snprintf(id, GSS_CTX_ID_MAXLEN,  "gss-ctx_%d", uid);
+
+        /* Search the session keyring. Note that the user key create
+         * needs to also use the same keyring. */
+	rkey = keyring_search(make_key_ref(cred->user->session_keyring, 1),
+				&key_type_gss_ctx, id);
+	kfree(id);
+
+	if (IS_ERR(rkey))
+		goto out;
+
+	key = key_ref_to_ptr(rkey);
+
+	rcu_read_lock();
+	key->perm |= KEY_USR_VIEW;
+
+	ret = key_validate(key);
+	if (ret < 0)
+		goto out_unlock;
+
+	payload = rcu_dereference(key->payload.data);
+	if (IS_ERR_OR_NULL(payload))
+		goto out_unlock;
+
+	if (key->uid != uid || payload->datalen <= 0)
+		goto out_unlock;
+
+	msg->cc_name = kzalloc(payload->datalen + 1, GFP_NOFS);
+	if (!msg->cc_name)
+		goto out_unlock;
+
+	memcpy(msg->cc_name, payload->data, payload->datalen);
+	msg->key = key;
+	dprintk("RPC    krb5 ccache %s has gss-ctx key with serial %u \n",
+		msg->cc_name, msg->key->serial);
+out_unlock:
+	rcu_read_unlock();
+out:
+	return;
+}
+
 static void
 gss_upcall_callback(struct rpc_task *task)
 {
@@ -478,6 +552,11 @@ static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,
 		p += len;
 		gss_msg->msg.len += len;
 	}
+	if (gss_msg->cc_name != NULL) {
+		len = sprintf(p, "ccache=%s ", gss_msg->cc_name);
+		p += len;
+		gss_msg->msg.len += len;
+	}
 	len = sprintf(p, "\n");
 	gss_msg->msg.len += len;
 
@@ -500,6 +579,9 @@ gss_alloc_msg(struct gss_auth *gss_auth,
 		kfree(gss_msg);
 		return ERR_PTR(vers);
 	}
+	/* Use a gss-ctx key if available */
+	gss_find_ctx_key(uid, gss_msg);
+
 	gss_msg->pipe = gss_auth->gss_pipe[vers]->pipe;
 	INIT_LIST_HEAD(&gss_msg->list);
 	rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");
@@ -532,6 +614,9 @@ gss_setup_upcall(struct gss_auth *gss_auth, struct rpc_cred *cred)
 		int res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg);
 		if (res) {
 			gss_unhash_msg(gss_new);
+			if (gss_new->key)
+				/* balance key_get in keyring_search */
+				key_put(gss_new->key);
 			gss_msg = ERR_PTR(res);
 		}
 	} else
@@ -654,7 +739,7 @@ retry:
 		schedule();
 	}
 	if (gss_msg->ctx)
-		gss_cred_set_ctx(cred, gss_msg->ctx);
+		gss_cred_set_ctx(cred, gss_msg->ctx, gss_msg->key);
 	else
 		err = gss_msg->msg.errno;
 	spin_unlock(&pipe->lock);
