From: Trond Myklebust Date: Wed, 18 Jul 2007 10:24:14 -0400 SUNRPC: Cache rpcsec_gss upcall failures in order to back off retries Do an exponential back off if the previous upcall failed to return a valid GSS context. Signed-off-by: Trond Myklebust --- include/linux/sunrpc/auth_gss.h | 2 ++ net/sunrpc/auth_gss/auth_gss.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 0 deletions(-) diff --git a/include/linux/sunrpc/auth_gss.h b/include/linux/sunrpc/auth_gss.h index 67658e1..8c19bef 100644 --- a/include/linux/sunrpc/auth_gss.h +++ b/include/linux/sunrpc/auth_gss.h @@ -84,6 +84,8 @@ struct gss_cred { enum rpc_gss_svc gc_service; struct gss_cl_ctx *gc_ctx; struct gss_upcall_msg *gc_upcall; + unsigned long gc_last_upcall; + unsigned int gc_retry_delay; }; #endif /* __KERNEL__ */ diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 6dac387..743b3c2 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -70,6 +70,9 @@ static const struct rpc_credops gss_nullops; * using integrity (two 4-byte integers): */ #define GSS_VERF_SLACK 100 +#define GSS_RETRY_UPCALL_MAX_DELAY (15 * HZ) +#define GSS_RETRY_UPCALL_INIT_DELAY (HZ >> 1) + /* XXX this define must match the gssd define * as it is passed to gssd to signal the use of * machine creds should be part of the shared rpc interface */ @@ -817,6 +820,7 @@ gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) */ cred->gc_base.cr_flags = 1UL << RPCAUTH_CRED_NEW; cred->gc_service = gss_auth->service; + cred->gc_last_upcall = jiffies - 1; kref_get(&gss_auth->kref); return &cred->gc_base; @@ -826,6 +830,35 @@ out_err: } static int +gss_delay_retry(unsigned long last_upcall, unsigned long retry_delay) +{ + unsigned long now = jiffies; + unsigned long timeout; + + if (time_before(now, last_upcall)) + return 0; + timeout = last_upcall + retry_delay; + if (time_after(now, timeout)) + return 0; + if (schedule_timeout_interruptible(timeout - now) == 0) + return 0; + return -ERESTARTSYS; +} + +static void +gss_update_retry_delay(struct gss_cred *gss_cred, int err) +{ + gss_cred->gc_last_upcall = jiffies; + if (err == 0) { + gss_cred->gc_retry_delay = GSS_RETRY_UPCALL_INIT_DELAY; + return; + } + gss_cred->gc_retry_delay <<= 1; + if (gss_cred->gc_retry_delay > GSS_RETRY_UPCALL_MAX_DELAY) + gss_cred->gc_retry_delay = GSS_RETRY_UPCALL_MAX_DELAY; +} + +static int gss_cred_init(struct rpc_auth *auth, struct rpc_cred *cred) { struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth); @@ -833,7 +866,11 @@ gss_cred_init(struct rpc_auth *auth, struct rpc_cred *cred) int err; do { + err = gss_delay_retry(gss_cred->gc_last_upcall, gss_cred->gc_retry_delay); + if (err < 0) + break; err = gss_create_upcall(gss_auth, gss_cred); + gss_update_retry_delay(gss_cred, err); } while (err == -EAGAIN); return err; }