SUNRPC: Don't spam gssd with upcall requests when the kerberos key expired
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Thu, 13 May 2010 16:55:38 +0000 (12:55 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Fri, 14 May 2010 19:09:37 +0000 (15:09 -0400)
Now that the rpc.gssd daemon can explicitly tell us that the key expired,
we should cache that information to avoid spamming gssd.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
include/linux/sunrpc/auth.h
include/linux/sunrpc/auth_gss.h
net/sunrpc/auth_gss/auth_gss.c

index 996df4dac7d47860731ec76537720289593e1f65..87d7ec0bf779f549b82011166c59b8282d415574 100644 (file)
@@ -54,6 +54,7 @@ struct rpc_cred {
 #define RPCAUTH_CRED_NEW       0
 #define RPCAUTH_CRED_UPTODATE  1
 #define RPCAUTH_CRED_HASHED    2
+#define RPCAUTH_CRED_NEGATIVE  3
 
 #define RPCAUTH_CRED_MAGIC     0x0f4aa4f0
 
index d48d4e605f749bf70f6d75d6ceab7a39bbe4c821..671538d25bc15b623155f2b7b7fd269ce394b5d6 100644 (file)
@@ -82,6 +82,7 @@ struct gss_cred {
        enum rpc_gss_svc        gc_service;
        struct gss_cl_ctx       *gc_ctx;
        struct gss_upcall_msg   *gc_upcall;
+       unsigned long           gc_upcall_timestamp;
        unsigned char           gc_machine_cred : 1;
 };
 
index 48a7939dc9e268d7ad5c2f553f8b06d834a7368b..8da2a0e68574d80bf7e3e37ca6814d804015fbf7 100644 (file)
@@ -57,6 +57,9 @@ static const struct rpc_authops authgss_ops;
 static const struct rpc_credops gss_credops;
 static const struct rpc_credops gss_nullops;
 
+#define GSS_RETRY_EXPIRED 5
+static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED;
+
 #ifdef RPC_DEBUG
 # define RPCDBG_FACILITY       RPCDBG_AUTH
 #endif
@@ -349,6 +352,24 @@ gss_unhash_msg(struct gss_upcall_msg *gss_msg)
        spin_unlock(&inode->i_lock);
 }
 
+static void
+gss_handle_downcall_result(struct gss_cred *gss_cred, struct gss_upcall_msg *gss_msg)
+{
+       switch (gss_msg->msg.errno) {
+       case 0:
+               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);
+               break;
+       case -EKEYEXPIRED:
+               set_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags);
+       }
+       gss_cred->gc_upcall_timestamp = jiffies;
+       gss_cred->gc_upcall = NULL;
+       rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno);
+}
+
 static void
 gss_upcall_callback(struct rpc_task *task)
 {
@@ -358,13 +379,9 @@ gss_upcall_callback(struct rpc_task *task)
        struct inode *inode = &gss_msg->inode->vfs_inode;
 
        spin_lock(&inode->i_lock);
-       if (gss_msg->ctx)
-               gss_cred_set_ctx(task->tk_msg.rpc_cred, gss_msg->ctx);
-       else
-               task->tk_status = gss_msg->msg.errno;
-       gss_cred->gc_upcall = NULL;
-       rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno);
+       gss_handle_downcall_result(gss_cred, gss_msg);
        spin_unlock(&inode->i_lock);
+       task->tk_status = gss_msg->msg.errno;
        gss_release_msg(gss_msg);
 }
 
@@ -513,18 +530,16 @@ gss_refresh_upcall(struct rpc_task *task)
        spin_lock(&inode->i_lock);
        if (gss_cred->gc_upcall != NULL)
                rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL);
-       else if (gss_msg->ctx != NULL) {
-               gss_cred_set_ctx(task->tk_msg.rpc_cred, gss_msg->ctx);
-               gss_cred->gc_upcall = NULL;
-               rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno);
-       } else if (gss_msg->msg.errno >= 0) {
+       else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) {
                task->tk_timeout = 0;
                gss_cred->gc_upcall = gss_msg;
                /* gss_upcall_callback will release the reference to gss_upcall_msg */
                atomic_inc(&gss_msg->count);
                rpc_sleep_on(&gss_msg->rpc_waitqueue, task, gss_upcall_callback);
-       } else
+       } else {
+               gss_handle_downcall_result(gss_cred, gss_msg);
                err = gss_msg->msg.errno;
+       }
        spin_unlock(&inode->i_lock);
        gss_release_msg(gss_msg);
 out:
@@ -1123,6 +1138,23 @@ static int gss_renew_cred(struct rpc_task *task)
        return 0;
 }
 
+static int gss_cred_is_negative_entry(struct rpc_cred *cred)
+{
+       if (test_bit(RPCAUTH_CRED_NEGATIVE, &cred->cr_flags)) {
+               unsigned long now = jiffies;
+               unsigned long begin, expire;
+               struct gss_cred *gss_cred; 
+
+               gss_cred = container_of(cred, struct gss_cred, gc_base);
+               begin = gss_cred->gc_upcall_timestamp;
+               expire = begin + gss_expired_cred_retry_delay * HZ;
+
+               if (time_in_range_open(now, begin, expire))
+                       return 1;
+       }
+       return 0;
+}
+
 /*
 * Refresh credentials. XXX - finish
 */
@@ -1132,6 +1164,9 @@ gss_refresh(struct rpc_task *task)
        struct rpc_cred *cred = task->tk_msg.rpc_cred;
        int ret = 0;
 
+       if (gss_cred_is_negative_entry(cred))
+               return -EKEYEXPIRED;
+
        if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) &&
                        !test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags)) {
                ret = gss_renew_cred(task);
@@ -1585,5 +1620,11 @@ static void __exit exit_rpcsec_gss(void)
 }
 
 MODULE_LICENSE("GPL");
+module_param_named(expired_cred_retry_delay,
+                  gss_expired_cred_retry_delay,
+                  uint, 0644);
+MODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until "
+               "the RPC engine retries an expired credential");
+
 module_init(init_rpcsec_gss)
 module_exit(exit_rpcsec_gss)