From b39e625b6e75aa70e26c13f9378756bb5f2af032 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 11 Jun 2007 23:05:07 -0400 Subject: NFSv4: Clean up nfs4_call_async() Use rpc_run_task() instead of doing it ourselves. Signed-off-by: Trond Myklebust --- net/sunrpc/sunrpc_syms.c | 1 - 1 file changed, 1 deletion(-) (limited to 'net') diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 73075de..c46d31c 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -30,7 +30,6 @@ EXPORT_SYMBOL(rpc_wake_up_next); EXPORT_SYMBOL(rpc_wake_up_task); EXPORT_SYMBOL(rpciod_down); EXPORT_SYMBOL(rpciod_up); -EXPORT_SYMBOL(rpc_new_task); EXPORT_SYMBOL(rpc_wake_up_status); /* RPC client functions */ -- cgit v1.1 From 6529eba08fe7297852391a468d95322913de73fa Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 14 Jun 2007 16:40:14 -0400 Subject: SUNRPC: Move rpc_task->tk_task list into struct rpc_clnt Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 5 +++ net/sunrpc/sched.c | 117 +++++++++++++++++++++++++++++++++-------------------- 2 files changed, 79 insertions(+), 43 deletions(-) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index d8fbee4..6631ece 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -148,6 +148,7 @@ static struct rpc_clnt * rpc_new_client(struct rpc_xprt *xprt, char *servname, s if (clnt->cl_metrics == NULL) goto out_no_stats; clnt->cl_program = program; + INIT_LIST_HEAD(&clnt->cl_tasks); if (!xprt_bound(clnt->cl_xprt)) clnt->cl_autobind = 1; @@ -172,6 +173,7 @@ static struct rpc_clnt * rpc_new_client(struct rpc_xprt *xprt, char *servname, s if (clnt->cl_nodelen > UNX_MAXNODENAME) clnt->cl_nodelen = UNX_MAXNODENAME; memcpy(clnt->cl_nodename, utsname()->nodename, clnt->cl_nodelen); + rpc_register_client(clnt); return clnt; out_no_auth: @@ -283,9 +285,11 @@ rpc_clone_client(struct rpc_clnt *clnt) new->cl_autobind = 0; new->cl_oneshot = 0; new->cl_dead = 0; + INIT_LIST_HEAD(&new->cl_tasks); rpc_init_rtt(&new->cl_rtt_default, clnt->cl_xprt->timeout.to_initval); if (new->cl_auth) atomic_inc(&new->cl_auth->au_count); + rpc_register_client(new); return new; out_no_path: rpc_free_iostats(new->cl_metrics); @@ -357,6 +361,7 @@ rpc_destroy_client(struct rpc_clnt *clnt) if (clnt->cl_server != clnt->cl_inline_name) kfree(clnt->cl_server); out_free: + rpc_unregister_client(clnt); rpc_free_iostats(clnt->cl_metrics); clnt->cl_metrics = NULL; xprt_put(clnt->cl_xprt); diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 944d753..6309f3b 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -50,9 +50,10 @@ static void rpc_release_task(struct rpc_task *task); static RPC_WAITQ(delay_queue, "delayq"); /* - * All RPC tasks are linked into this list + * All RPC clients are linked into this list */ -static LIST_HEAD(all_tasks); +static LIST_HEAD(all_clients); +static DECLARE_WAIT_QUEUE_HEAD(client_kill_wait); /* * rpciod-related stuff @@ -277,7 +278,8 @@ static void rpc_set_active(struct rpc_task *task) task->tk_pid = rpc_task_id++; #endif /* Add to global list of all tasks */ - list_add_tail(&task->tk_task, &all_tasks); + if (task->tk_client) + list_add_tail(&task->tk_task, &task->tk_client->cl_tasks); spin_unlock(&rpc_sched_lock); } @@ -818,6 +820,7 @@ void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, int flags, cons if (tk_ops->rpc_call_prepare != NULL) task->tk_action = rpc_prepare_task; task->tk_calldata = calldata; + INIT_LIST_HEAD(&task->tk_task); /* Initialize retry counters */ task->tk_garb_retry = 2; @@ -920,11 +923,12 @@ static void rpc_release_task(struct rpc_task *task) #endif dprintk("RPC: %5u release task\n", task->tk_pid); - /* Remove from global task list */ - spin_lock(&rpc_sched_lock); - list_del(&task->tk_task); - spin_unlock(&rpc_sched_lock); - + if (!list_empty(&task->tk_task)) { + /* Remove from client task list */ + spin_lock(&rpc_sched_lock); + list_del(&task->tk_task); + spin_unlock(&rpc_sched_lock); + } BUG_ON (RPC_IS_QUEUED(task)); /* Synchronously delete any running timer */ @@ -966,42 +970,52 @@ EXPORT_SYMBOL(rpc_run_task); * Kill all tasks for the given client. * XXX: kill their descendants as well? */ -void rpc_killall_tasks(struct rpc_clnt *clnt) +static void rpc_killall_tasks_locked(struct list_head *head) { struct rpc_task *rovr; - struct list_head *le; - dprintk("RPC: killing all tasks for client %p\n", clnt); - /* - * Spin lock all_tasks to prevent changes... - */ - spin_lock(&rpc_sched_lock); - alltask_for_each(rovr, le, &all_tasks) { + list_for_each_entry(rovr, head, tk_task) { if (! RPC_IS_ACTIVATED(rovr)) continue; - if (!clnt || rovr->tk_client == clnt) { + if (!(rovr->tk_flags & RPC_TASK_KILLED)) { rovr->tk_flags |= RPC_TASK_KILLED; rpc_exit(rovr, -EIO); rpc_wake_up_task(rovr); } } +} + +void rpc_killall_tasks(struct rpc_clnt *clnt) +{ + dprintk("RPC: killing all tasks for client %p\n", clnt); + /* + * Spin lock all_tasks to prevent changes... + */ + spin_lock(&rpc_sched_lock); + rpc_killall_tasks_locked(&clnt->cl_tasks); spin_unlock(&rpc_sched_lock); } static void rpciod_killall(void) { + struct rpc_clnt *clnt; unsigned long flags; - while (!list_empty(&all_tasks)) { + for(;;) { clear_thread_flag(TIF_SIGPENDING); - rpc_killall_tasks(NULL); + + spin_lock(&rpc_sched_lock); + list_for_each_entry(clnt, &all_clients, cl_clients) + rpc_killall_tasks_locked(&clnt->cl_tasks); + spin_unlock(&rpc_sched_lock); flush_workqueue(rpciod_workqueue); - if (!list_empty(&all_tasks)) { - dprintk("RPC: rpciod_killall: waiting for tasks " + if (!list_empty(&all_clients)) + break; + dprintk("RPC: rpciod_killall: waiting for tasks " "to exit\n"); - yield(); - } + wait_event_timeout(client_kill_wait, + list_empty(&all_clients), 1*HZ); } spin_lock_irqsave(¤t->sighand->siglock, flags); @@ -1009,6 +1023,22 @@ static void rpciod_killall(void) spin_unlock_irqrestore(¤t->sighand->siglock, flags); } +void rpc_register_client(struct rpc_clnt *clnt) +{ + spin_lock(&rpc_sched_lock); + list_add(&clnt->cl_clients, &all_clients); + spin_unlock(&rpc_sched_lock); +} + +void rpc_unregister_client(struct rpc_clnt *clnt) +{ + spin_lock(&rpc_sched_lock); + list_del(&clnt->cl_clients); + if (list_empty(&all_clients)) + wake_up(&client_kill_wait); + spin_unlock(&rpc_sched_lock); +} + /* * Start up the rpciod process if it's not already running. */ @@ -1071,32 +1101,33 @@ rpciod_down(void) #ifdef RPC_DEBUG void rpc_show_tasks(void) { - struct list_head *le; + struct rpc_clnt *clnt; struct rpc_task *t; spin_lock(&rpc_sched_lock); - if (list_empty(&all_tasks)) { - spin_unlock(&rpc_sched_lock); - return; - } + if (list_empty(&all_clients)) + goto out; printk("-pid- proc flgs status -client- -prog- --rqstp- -timeout " "-rpcwait -action- ---ops--\n"); - alltask_for_each(t, le, &all_tasks) { - const char *rpc_waitq = "none"; - - if (RPC_IS_QUEUED(t)) - rpc_waitq = rpc_qname(t->u.tk_wait.rpc_waitq); - - printk("%5u %04d %04x %6d %8p %6d %8p %8ld %8s %8p %8p\n", - t->tk_pid, - (t->tk_msg.rpc_proc ? t->tk_msg.rpc_proc->p_proc : -1), - t->tk_flags, t->tk_status, - t->tk_client, - (t->tk_client ? t->tk_client->cl_prog : 0), - t->tk_rqstp, t->tk_timeout, - rpc_waitq, - t->tk_action, t->tk_ops); + list_for_each_entry(clnt, &all_clients, cl_clients) { + list_for_each_entry(t, &clnt->cl_tasks, tk_task) { + const char *rpc_waitq = "none"; + + if (RPC_IS_QUEUED(t)) + rpc_waitq = rpc_qname(t->u.tk_wait.rpc_waitq); + + printk("%5u %04d %04x %6d %8p %6d %8p %8ld %8s %8p %8p\n", + t->tk_pid, + (t->tk_msg.rpc_proc ? t->tk_msg.rpc_proc->p_proc : -1), + t->tk_flags, t->tk_status, + t->tk_client, + (t->tk_client ? t->tk_client->cl_prog : 0), + t->tk_rqstp, t->tk_timeout, + rpc_waitq, + t->tk_action, t->tk_ops); + } } +out: spin_unlock(&rpc_sched_lock); } #endif -- cgit v1.1 From 4bef61ff7514396419563ca54fd42ef846485b06 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 16 Jun 2007 14:17:01 -0400 Subject: SUNRPC: Add a per-rpc_clnt spinlock Use that to protect the rpc_clnt->cl_tasks list instead of using a global lock. Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 2 ++ net/sunrpc/sched.c | 47 +++++++++++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 20 deletions(-) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 6631ece..424dfdc 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -149,6 +149,7 @@ static struct rpc_clnt * rpc_new_client(struct rpc_xprt *xprt, char *servname, s goto out_no_stats; clnt->cl_program = program; INIT_LIST_HEAD(&clnt->cl_tasks); + spin_lock_init(&clnt->cl_lock); if (!xprt_bound(clnt->cl_xprt)) clnt->cl_autobind = 1; @@ -286,6 +287,7 @@ rpc_clone_client(struct rpc_clnt *clnt) new->cl_oneshot = 0; new->cl_dead = 0; INIT_LIST_HEAD(&new->cl_tasks); + spin_lock_init(&new->cl_lock); rpc_init_rtt(&new->cl_rtt_default, clnt->cl_xprt->timeout.to_initval); if (new->cl_auth) atomic_inc(&new->cl_auth->au_count); diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 6309f3b..f56ebc5 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -270,17 +270,22 @@ static int rpc_wait_bit_interruptible(void *word) static void rpc_set_active(struct rpc_task *task) { + struct rpc_clnt *clnt; if (test_and_set_bit(RPC_TASK_ACTIVE, &task->tk_runstate) != 0) return; - spin_lock(&rpc_sched_lock); #ifdef RPC_DEBUG task->tk_magic = RPC_TASK_MAGIC_ID; + spin_lock(&rpc_sched_lock); task->tk_pid = rpc_task_id++; + spin_unlock(&rpc_sched_lock); #endif /* Add to global list of all tasks */ - if (task->tk_client) - list_add_tail(&task->tk_task, &task->tk_client->cl_tasks); - spin_unlock(&rpc_sched_lock); + clnt = task->tk_client; + if (clnt != NULL) { + spin_lock(&clnt->cl_lock); + list_add_tail(&task->tk_task, &clnt->cl_tasks); + spin_unlock(&clnt->cl_lock); + } } /* @@ -924,10 +929,11 @@ static void rpc_release_task(struct rpc_task *task) dprintk("RPC: %5u release task\n", task->tk_pid); if (!list_empty(&task->tk_task)) { + struct rpc_clnt *clnt = task->tk_client; /* Remove from client task list */ - spin_lock(&rpc_sched_lock); + spin_lock(&clnt->cl_lock); list_del(&task->tk_task); - spin_unlock(&rpc_sched_lock); + spin_unlock(&clnt->cl_lock); } BUG_ON (RPC_IS_QUEUED(task)); @@ -970,12 +976,19 @@ EXPORT_SYMBOL(rpc_run_task); * Kill all tasks for the given client. * XXX: kill their descendants as well? */ -static void rpc_killall_tasks_locked(struct list_head *head) +void rpc_killall_tasks(struct rpc_clnt *clnt) { struct rpc_task *rovr; - list_for_each_entry(rovr, head, tk_task) { + if (list_empty(&clnt->cl_tasks)) + return; + dprintk("RPC: killing all tasks for client %p\n", clnt); + /* + * Spin lock all_tasks to prevent changes... + */ + spin_lock(&clnt->cl_lock); + list_for_each_entry(rovr, &clnt->cl_tasks, tk_task) { if (! RPC_IS_ACTIVATED(rovr)) continue; if (!(rovr->tk_flags & RPC_TASK_KILLED)) { @@ -984,17 +997,7 @@ static void rpc_killall_tasks_locked(struct list_head *head) rpc_wake_up_task(rovr); } } -} - -void rpc_killall_tasks(struct rpc_clnt *clnt) -{ - dprintk("RPC: killing all tasks for client %p\n", clnt); - /* - * Spin lock all_tasks to prevent changes... - */ - spin_lock(&rpc_sched_lock); - rpc_killall_tasks_locked(&clnt->cl_tasks); - spin_unlock(&rpc_sched_lock); + spin_unlock(&clnt->cl_lock); } static void rpciod_killall(void) @@ -1007,7 +1010,7 @@ static void rpciod_killall(void) spin_lock(&rpc_sched_lock); list_for_each_entry(clnt, &all_clients, cl_clients) - rpc_killall_tasks_locked(&clnt->cl_tasks); + rpc_killall_tasks(clnt); spin_unlock(&rpc_sched_lock); flush_workqueue(rpciod_workqueue); if (!list_empty(&all_clients)) @@ -1110,6 +1113,9 @@ void rpc_show_tasks(void) printk("-pid- proc flgs status -client- -prog- --rqstp- -timeout " "-rpcwait -action- ---ops--\n"); list_for_each_entry(clnt, &all_clients, cl_clients) { + if (list_empty(&clnt->cl_tasks)) + continue; + spin_lock(&clnt->cl_lock); list_for_each_entry(t, &clnt->cl_tasks, tk_task) { const char *rpc_waitq = "none"; @@ -1126,6 +1132,7 @@ void rpc_show_tasks(void) rpc_waitq, t->tk_action, t->tk_ops); } + spin_unlock(&clnt->cl_lock); } out: spin_unlock(&rpc_sched_lock); -- cgit v1.1 From c44fe705530ff9ea5e563bf9b65bdd29defe682b Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 16 Jun 2007 14:17:01 -0400 Subject: SUNRPC: Clean up tk_pid allocation and make it lockless Signed-off-by: Trond Myklebust --- net/sunrpc/sched.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index f56ebc5..0e9fbbd 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -25,7 +25,6 @@ #ifdef RPC_DEBUG #define RPCDBG_FACILITY RPCDBG_SCHED #define RPC_TASK_MAGIC_ID 0xf00baa -static int rpc_task_id; #endif /* @@ -268,17 +267,26 @@ static int rpc_wait_bit_interruptible(void *word) return 0; } +#ifdef RPC_DEBUG +static void rpc_task_set_debuginfo(struct rpc_task *task) +{ + static atomic_t rpc_pid; + + task->tk_magic = RPC_TASK_MAGIC_ID; + task->tk_pid = atomic_inc_return(&rpc_pid); +} +#else +static inline void rpc_task_set_debuginfo(struct rpc_task *task) +{ +} +#endif + static void rpc_set_active(struct rpc_task *task) { struct rpc_clnt *clnt; if (test_and_set_bit(RPC_TASK_ACTIVE, &task->tk_runstate) != 0) return; -#ifdef RPC_DEBUG - task->tk_magic = RPC_TASK_MAGIC_ID; - spin_lock(&rpc_sched_lock); - task->tk_pid = rpc_task_id++; - spin_unlock(&rpc_sched_lock); -#endif + rpc_task_set_debuginfo(task); /* Add to global list of all tasks */ clnt = task->tk_client; if (clnt != NULL) { -- cgit v1.1 From 34f52e3591f241b825353ba27def956d8487c400 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 14 Jun 2007 16:40:31 -0400 Subject: SUNRPC: Convert rpc_clnt->cl_users to a kref Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 57 +++++++++++++++++++++++++-------------------------- net/sunrpc/rpc_pipe.c | 2 +- net/sunrpc/sched.c | 6 ++---- 3 files changed, 31 insertions(+), 34 deletions(-) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 424dfdc..254a6e1 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -121,7 +121,6 @@ static struct rpc_clnt * rpc_new_client(struct rpc_xprt *xprt, char *servname, s clnt = kzalloc(sizeof(*clnt), GFP_KERNEL); if (!clnt) goto out_err; - atomic_set(&clnt->cl_users, 0); atomic_set(&clnt->cl_count, 1); clnt->cl_parent = clnt; @@ -157,6 +156,8 @@ static struct rpc_clnt * rpc_new_client(struct rpc_xprt *xprt, char *servname, s clnt->cl_rtt = &clnt->cl_rtt_default; rpc_init_rtt(&clnt->cl_rtt_default, xprt->timeout.to_initval); + kref_init(&clnt->cl_kref); + err = rpc_setup_pipedir(clnt, program->pipe_dir_name); if (err < 0) goto out_no_path; @@ -272,10 +273,10 @@ rpc_clone_client(struct rpc_clnt *clnt) if (!new) goto out_no_clnt; atomic_set(&new->cl_count, 1); - atomic_set(&new->cl_users, 0); new->cl_metrics = rpc_alloc_iostats(clnt); if (new->cl_metrics == NULL) goto out_no_stats; + kref_init(&new->cl_kref); err = rpc_setup_pipedir(new, clnt->cl_program->pipe_dir_name); if (err != 0) goto out_no_path; @@ -311,40 +312,28 @@ out_no_clnt: int rpc_shutdown_client(struct rpc_clnt *clnt) { - dprintk("RPC: shutting down %s client for %s, tasks=%d\n", - clnt->cl_protname, clnt->cl_server, - atomic_read(&clnt->cl_users)); + dprintk("RPC: shutting down %s client for %s\n", + clnt->cl_protname, clnt->cl_server); - while (atomic_read(&clnt->cl_users) > 0) { + while (!list_empty(&clnt->cl_tasks)) { /* Don't let rpc_release_client destroy us */ clnt->cl_oneshot = 0; clnt->cl_dead = 0; rpc_killall_tasks(clnt); wait_event_timeout(destroy_wait, - !atomic_read(&clnt->cl_users), 1*HZ); - } - - if (atomic_read(&clnt->cl_users) < 0) { - printk(KERN_ERR "RPC: rpc_shutdown_client clnt %p tasks=%d\n", - clnt, atomic_read(&clnt->cl_users)); -#ifdef RPC_DEBUG - rpc_show_tasks(); -#endif - BUG(); + list_empty(&clnt->cl_tasks), 1*HZ); } return rpc_destroy_client(clnt); } /* - * Delete an RPC client + * Free an RPC client */ -int -rpc_destroy_client(struct rpc_clnt *clnt) +static void +rpc_free_client(struct kref *kref) { - if (!atomic_dec_and_test(&clnt->cl_count)) - return 1; - BUG_ON(atomic_read(&clnt->cl_users) != 0); + struct rpc_clnt *clnt = container_of(kref, struct rpc_clnt, cl_kref); dprintk("RPC: destroying %s client for %s\n", clnt->cl_protname, clnt->cl_server); @@ -368,23 +357,33 @@ out_free: clnt->cl_metrics = NULL; xprt_put(clnt->cl_xprt); kfree(clnt); - return 0; } /* - * Release an RPC client + * Release reference to the RPC client */ void rpc_release_client(struct rpc_clnt *clnt) { - dprintk("RPC: rpc_release_client(%p, %d)\n", - clnt, atomic_read(&clnt->cl_users)); + dprintk("RPC: rpc_release_client(%p)\n", clnt); - if (!atomic_dec_and_test(&clnt->cl_users)) - return; - wake_up(&destroy_wait); + if (list_empty(&clnt->cl_tasks)) + wake_up(&destroy_wait); if (clnt->cl_oneshot || clnt->cl_dead) rpc_destroy_client(clnt); + kref_put(&clnt->cl_kref, rpc_free_client); +} + +/* + * Delete an RPC client + */ +int +rpc_destroy_client(struct rpc_clnt *clnt) +{ + if (!atomic_dec_and_test(&clnt->cl_count)) + return 1; + kref_put(&clnt->cl_kref, rpc_free_client); + return 0; } /** diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 5887457d..826190d 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -344,7 +344,7 @@ rpc_info_open(struct inode *inode, struct file *file) mutex_lock(&inode->i_mutex); clnt = RPC_I(inode)->private; if (clnt) { - atomic_inc(&clnt->cl_users); + kref_get(&clnt->cl_kref); m->private = clnt; } else { single_release(inode, file); diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 0e9fbbd..bb12983 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -846,7 +846,7 @@ void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, int flags, cons task->tk_workqueue = rpciod_workqueue; if (clnt) { - atomic_inc(&clnt->cl_users); + kref_get(&clnt->cl_kref); if (clnt->cl_softrtry) task->tk_flags |= RPC_TASK_SOFT; if (!clnt->cl_intr) @@ -898,9 +898,7 @@ out: cleanup: /* Check whether to release the client */ if (clnt) { - printk("rpc_new_task: failed, users=%d, oneshot=%d\n", - atomic_read(&clnt->cl_users), clnt->cl_oneshot); - atomic_inc(&clnt->cl_users); /* pretend we were used ... */ + kref_get(&clnt->cl_kref); /* pretend we were used ... */ rpc_release_client(clnt); } goto out; -- cgit v1.1 From 848f1fe6be2e290691bb6c13cbb8fd92bd0cfaab Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 9 Jun 2007 19:39:12 -0400 Subject: SUNRPC: Kill rpc_clnt->cl_dead Its use is at best racy, and there is only one user (lockd), which has additional locking that makes the whole thing redundant. Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 254a6e1..fb65249 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -286,7 +286,6 @@ rpc_clone_client(struct rpc_clnt *clnt) /* Turn off autobind on clones */ new->cl_autobind = 0; new->cl_oneshot = 0; - new->cl_dead = 0; INIT_LIST_HEAD(&new->cl_tasks); spin_lock_init(&new->cl_lock); rpc_init_rtt(&new->cl_rtt_default, clnt->cl_xprt->timeout.to_initval); @@ -305,9 +304,8 @@ out_no_clnt: /* * Properly shut down an RPC client, terminating all outstanding - * requests. Note that we must be certain that cl_oneshot and - * cl_dead are cleared, or else the client would be destroyed - * when the last task releases it. + * requests. Note that we must be certain that cl_oneshot is cleared, + * or else the client would be destroyed when the last task releases it. */ int rpc_shutdown_client(struct rpc_clnt *clnt) @@ -318,7 +316,6 @@ rpc_shutdown_client(struct rpc_clnt *clnt) while (!list_empty(&clnt->cl_tasks)) { /* Don't let rpc_release_client destroy us */ clnt->cl_oneshot = 0; - clnt->cl_dead = 0; rpc_killall_tasks(clnt); wait_event_timeout(destroy_wait, list_empty(&clnt->cl_tasks), 1*HZ); @@ -369,7 +366,7 @@ rpc_release_client(struct rpc_clnt *clnt) if (list_empty(&clnt->cl_tasks)) wake_up(&destroy_wait); - if (clnt->cl_oneshot || clnt->cl_dead) + if (clnt->cl_oneshot) rpc_destroy_client(clnt); kref_put(&clnt->cl_kref, rpc_free_client); } @@ -483,10 +480,6 @@ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) sigset_t oldset; int status; - /* If this client is slain all further I/O fails */ - if (clnt->cl_dead) - return -EIO; - BUG_ON(flags & RPC_TASK_ASYNC); task = rpc_new_task(clnt, flags, &rpc_default_ops, NULL); @@ -519,11 +512,6 @@ rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags, sigset_t oldset; int status; - /* If this client is slain all further I/O fails */ - status = -EIO; - if (clnt->cl_dead) - goto out_release; - flags |= RPC_TASK_ASYNC; /* Create/initialize a new RPC task */ -- cgit v1.1 From 90c5755ff5111ffdcca10a1e8a823dba29f37b6d Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 9 Jun 2007 19:49:36 -0400 Subject: SUNRPC: Kill rpc_clnt->cl_oneshot Replace it with explicit calls to rpc_shutdown_client() or rpc_destroy_client() (for the case of asynchronous calls). Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 10 +--------- net/sunrpc/rpcb_clnt.c | 6 ++++-- net/sunrpc/sched.c | 14 ++------------ 3 files changed, 7 insertions(+), 23 deletions(-) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index fb65249..34662df 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -249,8 +249,6 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args) clnt->cl_intr = 1; if (args->flags & RPC_CLNT_CREATE_AUTOBIND) clnt->cl_autobind = 1; - if (args->flags & RPC_CLNT_CREATE_ONESHOT) - clnt->cl_oneshot = 1; if (args->flags & RPC_CLNT_CREATE_DISCRTRY) clnt->cl_discrtry = 1; @@ -285,7 +283,6 @@ rpc_clone_client(struct rpc_clnt *clnt) new->cl_xprt = xprt_get(clnt->cl_xprt); /* Turn off autobind on clones */ new->cl_autobind = 0; - new->cl_oneshot = 0; INIT_LIST_HEAD(&new->cl_tasks); spin_lock_init(&new->cl_lock); rpc_init_rtt(&new->cl_rtt_default, clnt->cl_xprt->timeout.to_initval); @@ -304,8 +301,7 @@ out_no_clnt: /* * Properly shut down an RPC client, terminating all outstanding - * requests. Note that we must be certain that cl_oneshot is cleared, - * or else the client would be destroyed when the last task releases it. + * requests. */ int rpc_shutdown_client(struct rpc_clnt *clnt) @@ -314,8 +310,6 @@ rpc_shutdown_client(struct rpc_clnt *clnt) clnt->cl_protname, clnt->cl_server); while (!list_empty(&clnt->cl_tasks)) { - /* Don't let rpc_release_client destroy us */ - clnt->cl_oneshot = 0; rpc_killall_tasks(clnt); wait_event_timeout(destroy_wait, list_empty(&clnt->cl_tasks), 1*HZ); @@ -366,8 +360,6 @@ rpc_release_client(struct rpc_clnt *clnt) if (list_empty(&clnt->cl_tasks)) wake_up(&destroy_wait); - if (clnt->cl_oneshot) - rpc_destroy_client(clnt); kref_put(&clnt->cl_kref, rpc_free_client); } diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 6c7aa8a..00853a3 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -184,8 +184,7 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr, .program = &rpcb_program, .version = version, .authflavor = RPC_AUTH_UNIX, - .flags = (RPC_CLNT_CREATE_ONESHOT | - RPC_CLNT_CREATE_NOPING), + .flags = RPC_CLNT_CREATE_NOPING, }; ((struct sockaddr_in *)srvaddr)->sin_port = htons(RPCBIND_PORT); @@ -238,6 +237,7 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) error = rpc_call_sync(rpcb_clnt, &msg, 0); + rpc_shutdown_client(rpcb_clnt); if (error < 0) printk(KERN_WARNING "RPC: failed to contact local rpcbind " "server (errno %d).\n", -error); @@ -286,6 +286,7 @@ int rpcb_getport_external(struct sockaddr_in *sin, __u32 prog, return PTR_ERR(rpcb_clnt); status = rpc_call_sync(rpcb_clnt, &msg, 0); + rpc_shutdown_client(rpcb_clnt); if (status >= 0) { if (map.r_port != 0) @@ -379,6 +380,7 @@ void rpcb_getport(struct rpc_task *task) } child = rpc_run_task(rpcb_clnt, RPC_TASK_ASYNC, &rpcb_getport_ops, map); + rpc_destroy_client(rpcb_clnt); if (IS_ERR(child)) { status = -EIO; dprintk("RPC: %5u rpcb_getport rpc_run_task failed\n", diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index bb12983..d95fe4e 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -876,9 +876,7 @@ static void rpc_free_task(struct rcu_head *rcu) } /* - * Create a new task for the specified client. We have to - * clean up after an allocation failure, as the client may - * have specified "oneshot". + * Create a new task for the specified client. */ struct rpc_task *rpc_new_task(struct rpc_clnt *clnt, int flags, const struct rpc_call_ops *tk_ops, void *calldata) { @@ -886,7 +884,7 @@ struct rpc_task *rpc_new_task(struct rpc_clnt *clnt, int flags, const struct rpc task = rpc_alloc_task(); if (!task) - goto cleanup; + goto out; rpc_init_task(task, clnt, flags, tk_ops, calldata); @@ -894,14 +892,6 @@ struct rpc_task *rpc_new_task(struct rpc_clnt *clnt, int flags, const struct rpc task->tk_flags |= RPC_TASK_DYNAMIC; out: return task; - -cleanup: - /* Check whether to release the client */ - if (clnt) { - kref_get(&clnt->cl_kref); /* pretend we were used ... */ - rpc_release_client(clnt); - } - goto out; } -- cgit v1.1 From 8ad7c892e18ff8e6df422eb48ca0f73268ffd632 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 14 Jun 2007 16:40:32 -0400 Subject: SUNRPC: Make rpc_clone take a reference instead of using cl_count Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 34662df..613c10e 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -279,7 +279,7 @@ rpc_clone_client(struct rpc_clnt *clnt) if (err != 0) goto out_no_path; new->cl_parent = clnt; - atomic_inc(&clnt->cl_count); + kref_get(&clnt->cl_kref); new->cl_xprt = xprt_get(clnt->cl_xprt); /* Turn off autobind on clones */ new->cl_autobind = 0; @@ -337,7 +337,7 @@ rpc_free_client(struct kref *kref) rpc_put_mount(); } if (clnt->cl_parent != clnt) { - rpc_destroy_client(clnt->cl_parent); + rpc_release_client(clnt->cl_parent); goto out_free; } if (clnt->cl_server != clnt->cl_inline_name) -- cgit v1.1 From 4c402b40970382ded616eadd544fd63feb76cc79 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 14 Jun 2007 16:40:32 -0400 Subject: SUNRPC: Remove rpc_clnt->cl_count The kref now does most of what cl_count + cl_user used to do. The only remaining role for cl_count is to tell us if we are in a 'shutdown' phase. We can provide that information using a single bit field instead of a full atomic counter. Also rename rpc_destroy_client() to rpc_close_client(), which reflects better what its role is these days. Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 19 ++----------------- net/sunrpc/rpcb_clnt.c | 2 +- net/sunrpc/sunrpc_syms.c | 1 - 3 files changed, 3 insertions(+), 19 deletions(-) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 613c10e..be5524d 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -121,7 +121,6 @@ static struct rpc_clnt * rpc_new_client(struct rpc_xprt *xprt, char *servname, s clnt = kzalloc(sizeof(*clnt), GFP_KERNEL); if (!clnt) goto out_err; - atomic_set(&clnt->cl_count, 1); clnt->cl_parent = clnt; clnt->cl_server = clnt->cl_inline_name; @@ -270,7 +269,6 @@ rpc_clone_client(struct rpc_clnt *clnt) new = kmemdup(clnt, sizeof(*new), GFP_KERNEL); if (!new) goto out_no_clnt; - atomic_set(&new->cl_count, 1); new->cl_metrics = rpc_alloc_iostats(clnt); if (new->cl_metrics == NULL) goto out_no_stats; @@ -303,8 +301,7 @@ out_no_clnt: * Properly shut down an RPC client, terminating all outstanding * requests. */ -int -rpc_shutdown_client(struct rpc_clnt *clnt) +void rpc_shutdown_client(struct rpc_clnt *clnt) { dprintk("RPC: shutting down %s client for %s\n", clnt->cl_protname, clnt->cl_server); @@ -315,7 +312,7 @@ rpc_shutdown_client(struct rpc_clnt *clnt) list_empty(&clnt->cl_tasks), 1*HZ); } - return rpc_destroy_client(clnt); + rpc_release_client(clnt); } /* @@ -363,18 +360,6 @@ rpc_release_client(struct rpc_clnt *clnt) kref_put(&clnt->cl_kref, rpc_free_client); } -/* - * Delete an RPC client - */ -int -rpc_destroy_client(struct rpc_clnt *clnt) -{ - if (!atomic_dec_and_test(&clnt->cl_count)) - return 1; - kref_put(&clnt->cl_kref, rpc_free_client); - return 0; -} - /** * rpc_bind_new_program - bind a new RPC program to an existing client * @old - old rpc_client diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 00853a3..cf7db59 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -380,7 +380,7 @@ void rpcb_getport(struct rpc_task *task) } child = rpc_run_task(rpcb_clnt, RPC_TASK_ASYNC, &rpcb_getport_ops, map); - rpc_destroy_client(rpcb_clnt); + rpc_release_client(rpcb_clnt); if (IS_ERR(child)) { status = -EIO; dprintk("RPC: %5u rpcb_getport rpc_run_task failed\n", diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index c46d31c..02e83e1 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -35,7 +35,6 @@ EXPORT_SYMBOL(rpc_wake_up_status); /* RPC client functions */ EXPORT_SYMBOL(rpc_clone_client); EXPORT_SYMBOL(rpc_bind_new_program); -EXPORT_SYMBOL(rpc_destroy_client); EXPORT_SYMBOL(rpc_shutdown_client); EXPORT_SYMBOL(rpc_killall_tasks); EXPORT_SYMBOL(rpc_call_sync); -- cgit v1.1 From d431a555fcf920e1b5c3e3eba52eb5f5e7836771 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sun, 17 Jun 2007 17:07:54 -0400 Subject: SUNRPC: Don't create an rpc_pipefs directory before rpc_clone is initialised Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index be5524d..78bbb35 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -269,6 +269,12 @@ rpc_clone_client(struct rpc_clnt *clnt) new = kmemdup(clnt, sizeof(*new), GFP_KERNEL); if (!new) goto out_no_clnt; + new->cl_parent = clnt; + /* Turn off autobind on clones */ + new->cl_autobind = 0; + INIT_LIST_HEAD(&new->cl_tasks); + spin_lock_init(&new->cl_lock); + rpc_init_rtt(&new->cl_rtt_default, clnt->cl_xprt->timeout.to_initval); new->cl_metrics = rpc_alloc_iostats(clnt); if (new->cl_metrics == NULL) goto out_no_stats; @@ -276,16 +282,10 @@ rpc_clone_client(struct rpc_clnt *clnt) err = rpc_setup_pipedir(new, clnt->cl_program->pipe_dir_name); if (err != 0) goto out_no_path; - new->cl_parent = clnt; - kref_get(&clnt->cl_kref); - new->cl_xprt = xprt_get(clnt->cl_xprt); - /* Turn off autobind on clones */ - new->cl_autobind = 0; - INIT_LIST_HEAD(&new->cl_tasks); - spin_lock_init(&new->cl_lock); - rpc_init_rtt(&new->cl_rtt_default, clnt->cl_xprt->timeout.to_initval); if (new->cl_auth) atomic_inc(&new->cl_auth->au_count); + xprt_get(clnt->cl_xprt); + kref_get(&clnt->cl_kref); rpc_register_client(new); return new; out_no_path: -- cgit v1.1 From ab418d70e1fceda1e2824c45ba3323a1b1413507 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 14 Jun 2007 17:08:36 -0400 Subject: SUNRPC: Optimise rpciod_up() Instead of taking the mutex every time we just need to increment/decrement rpciod_users, we can optmise by using atomic_inc_not_zero and atomic_dec_and_test. Signed-off-by: Trond Myklebust --- net/sunrpc/sched.c | 49 +++++++++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) (limited to 'net') diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index d95fe4e..f6eed4d 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -58,7 +58,7 @@ static DECLARE_WAIT_QUEUE_HEAD(client_kill_wait); * rpciod-related stuff */ static DEFINE_MUTEX(rpciod_mutex); -static unsigned int rpciod_users; +static atomic_t rpciod_users = ATOMIC_INIT(0); struct workqueue_struct *rpciod_workqueue; /* @@ -1047,28 +1047,27 @@ rpciod_up(void) struct workqueue_struct *wq; int error = 0; + if (atomic_inc_not_zero(&rpciod_users)) + return 0; + mutex_lock(&rpciod_mutex); - dprintk("RPC: rpciod_up: users %u\n", rpciod_users); - rpciod_users++; - if (rpciod_workqueue) - goto out; - /* - * If there's no pid, we should be the first user. - */ - if (rpciod_users > 1) - printk(KERN_WARNING "rpciod_up: no workqueue, %u users??\n", rpciod_users); + + /* Guard against races with rpciod_down() */ + if (rpciod_workqueue != NULL) + goto out_ok; /* * Create the rpciod thread and wait for it to start. */ + dprintk("RPC: creating workqueue rpciod\n"); error = -ENOMEM; wq = create_workqueue("rpciod"); - if (wq == NULL) { - printk(KERN_WARNING "rpciod_up: create workqueue failed, error=%d\n", error); - rpciod_users--; + if (wq == NULL) goto out; - } + rpciod_workqueue = wq; error = 0; +out_ok: + atomic_inc(&rpciod_users); out: mutex_unlock(&rpciod_mutex); return error; @@ -1077,23 +1076,17 @@ out: void rpciod_down(void) { + if (!atomic_dec_and_test(&rpciod_users)) + return; + mutex_lock(&rpciod_mutex); - dprintk("RPC: rpciod_down sema %u\n", rpciod_users); - if (rpciod_users) { - if (--rpciod_users) - goto out; - } else - printk(KERN_WARNING "rpciod_down: no users??\n"); + dprintk("RPC: destroying workqueue rpciod\n"); - if (!rpciod_workqueue) { - dprintk("RPC: rpciod_down: Nothing to do!\n"); - goto out; + if (atomic_read(&rpciod_users) == 0 && rpciod_workqueue != NULL) { + rpciod_killall(); + destroy_workqueue(rpciod_workqueue); + rpciod_workqueue = NULL; } - rpciod_killall(); - - destroy_workqueue(rpciod_workqueue); - rpciod_workqueue = NULL; - out: mutex_unlock(&rpciod_mutex); } -- cgit v1.1 From 4ada539ed77c7a2bbcb75cafbbd7bd8d2b9bef7b Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 14 Jun 2007 17:26:17 -0400 Subject: SUNRPC: Make create_client() take a reference to the rpciod workqueue Ensures that an rpc_client always has the possibility to send asynchronous RPC calls. Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 7 +++++++ net/sunrpc/sched.c | 31 ------------------------------- 2 files changed, 7 insertions(+), 31 deletions(-) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 78bbb35..fe838e9 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -111,6 +111,9 @@ static struct rpc_clnt * rpc_new_client(struct rpc_xprt *xprt, char *servname, s dprintk("RPC: creating %s client for %s (xprt %p)\n", program->name, servname, xprt); + err = rpciod_up(); + if (err) + goto out_no_rpciod; err = -EINVAL; if (!xprt) goto out_no_xprt; @@ -191,6 +194,8 @@ out_no_stats: out_err: xprt_put(xprt); out_no_xprt: + rpciod_down(); +out_no_rpciod: return ERR_PTR(err); } @@ -287,6 +292,7 @@ rpc_clone_client(struct rpc_clnt *clnt) xprt_get(clnt->cl_xprt); kref_get(&clnt->cl_kref); rpc_register_client(new); + rpciod_up(); return new; out_no_path: rpc_free_iostats(new->cl_metrics); @@ -344,6 +350,7 @@ out_free: rpc_free_iostats(clnt->cl_metrics); clnt->cl_metrics = NULL; xprt_put(clnt->cl_xprt); + rpciod_down(); kfree(clnt); } diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index f6eed4d..0582515 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -39,7 +39,6 @@ static mempool_t *rpc_task_mempool __read_mostly; static mempool_t *rpc_buffer_mempool __read_mostly; static void __rpc_default_timer(struct rpc_task *task); -static void rpciod_killall(void); static void rpc_async_schedule(struct work_struct *); static void rpc_release_task(struct rpc_task *task); @@ -52,7 +51,6 @@ static RPC_WAITQ(delay_queue, "delayq"); * All RPC clients are linked into this list */ static LIST_HEAD(all_clients); -static DECLARE_WAIT_QUEUE_HEAD(client_kill_wait); /* * rpciod-related stuff @@ -996,32 +994,6 @@ void rpc_killall_tasks(struct rpc_clnt *clnt) spin_unlock(&clnt->cl_lock); } -static void rpciod_killall(void) -{ - struct rpc_clnt *clnt; - unsigned long flags; - - for(;;) { - clear_thread_flag(TIF_SIGPENDING); - - spin_lock(&rpc_sched_lock); - list_for_each_entry(clnt, &all_clients, cl_clients) - rpc_killall_tasks(clnt); - spin_unlock(&rpc_sched_lock); - flush_workqueue(rpciod_workqueue); - if (!list_empty(&all_clients)) - break; - dprintk("RPC: rpciod_killall: waiting for tasks " - "to exit\n"); - wait_event_timeout(client_kill_wait, - list_empty(&all_clients), 1*HZ); - } - - spin_lock_irqsave(¤t->sighand->siglock, flags); - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); -} - void rpc_register_client(struct rpc_clnt *clnt) { spin_lock(&rpc_sched_lock); @@ -1033,8 +1005,6 @@ void rpc_unregister_client(struct rpc_clnt *clnt) { spin_lock(&rpc_sched_lock); list_del(&clnt->cl_clients); - if (list_empty(&all_clients)) - wake_up(&client_kill_wait); spin_unlock(&rpc_sched_lock); } @@ -1083,7 +1053,6 @@ rpciod_down(void) dprintk("RPC: destroying workqueue rpciod\n"); if (atomic_read(&rpciod_users) == 0 && rpciod_workqueue != NULL) { - rpciod_killall(); destroy_workqueue(rpciod_workqueue); rpciod_workqueue = NULL; } -- cgit v1.1 From f61534dfd38f895b203e2aadaba04f21a992ca8c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 14 Jun 2007 17:31:58 -0400 Subject: SUNRPC: Remove redundant calls to rpciod_up()/rpciod_down() Signed-off-by: Trond Myklebust --- net/sunrpc/sunrpc_syms.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'net') diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 02e83e1..b99b11b 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -28,8 +28,6 @@ EXPORT_SYMBOL(rpc_init_task); EXPORT_SYMBOL(rpc_sleep_on); EXPORT_SYMBOL(rpc_wake_up_next); EXPORT_SYMBOL(rpc_wake_up_task); -EXPORT_SYMBOL(rpciod_down); -EXPORT_SYMBOL(rpciod_up); EXPORT_SYMBOL(rpc_wake_up_status); /* RPC client functions */ -- cgit v1.1 From 188fef11db219f13f32d055ba59985e7d1a349fe Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 16 Jun 2007 14:18:40 -0400 Subject: SUNRPC: Move rpc_register_client and friends into net/sunrpc/clnt.c Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ net/sunrpc/sched.c | 62 ------------------------------------------------------ 2 files changed, 57 insertions(+), 62 deletions(-) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index fe838e9..4f39ab1 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -44,6 +44,12 @@ dprintk("RPC: %5u %s (status %d)\n", t->tk_pid, \ __FUNCTION__, t->tk_status) +/* + * All RPC clients are linked into this list + */ +static LIST_HEAD(all_clients); +static DEFINE_SPINLOCK(rpc_client_lock); + static DECLARE_WAIT_QUEUE_HEAD(destroy_wait); @@ -66,6 +72,19 @@ static void call_connect_status(struct rpc_task *task); static __be32 * call_header(struct rpc_task *task); static __be32 * call_verify(struct rpc_task *task); +static void rpc_register_client(struct rpc_clnt *clnt) +{ + spin_lock(&rpc_client_lock); + list_add(&clnt->cl_clients, &all_clients); + spin_unlock(&rpc_client_lock); +} + +static void rpc_unregister_client(struct rpc_clnt *clnt) +{ + spin_lock(&rpc_client_lock); + list_del(&clnt->cl_clients); + spin_unlock(&rpc_client_lock); +} static int rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name) @@ -1410,3 +1429,41 @@ int rpc_ping(struct rpc_clnt *clnt, int flags) put_rpccred(msg.rpc_cred); return err; } + +#ifdef RPC_DEBUG +void rpc_show_tasks(void) +{ + struct rpc_clnt *clnt; + struct rpc_task *t; + + spin_lock(&rpc_client_lock); + if (list_empty(&all_clients)) + goto out; + printk("-pid- proc flgs status -client- -prog- --rqstp- -timeout " + "-rpcwait -action- ---ops--\n"); + list_for_each_entry(clnt, &all_clients, cl_clients) { + if (list_empty(&clnt->cl_tasks)) + continue; + spin_lock(&clnt->cl_lock); + list_for_each_entry(t, &clnt->cl_tasks, tk_task) { + const char *rpc_waitq = "none"; + + if (RPC_IS_QUEUED(t)) + rpc_waitq = rpc_qname(t->u.tk_wait.rpc_waitq); + + printk("%5u %04d %04x %6d %8p %6d %8p %8ld %8s %8p %8p\n", + t->tk_pid, + (t->tk_msg.rpc_proc ? t->tk_msg.rpc_proc->p_proc : -1), + t->tk_flags, t->tk_status, + t->tk_client, + (t->tk_client ? t->tk_client->cl_prog : 0), + t->tk_rqstp, t->tk_timeout, + rpc_waitq, + t->tk_action, t->tk_ops); + } + spin_unlock(&clnt->cl_lock); + } +out: + spin_unlock(&rpc_client_lock); +} +#endif diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 0582515..c0f8d25 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -48,11 +48,6 @@ static void rpc_release_task(struct rpc_task *task); static RPC_WAITQ(delay_queue, "delayq"); /* - * All RPC clients are linked into this list - */ -static LIST_HEAD(all_clients); - -/* * rpciod-related stuff */ static DEFINE_MUTEX(rpciod_mutex); @@ -60,11 +55,6 @@ static atomic_t rpciod_users = ATOMIC_INIT(0); struct workqueue_struct *rpciod_workqueue; /* - * Spinlock for other critical sections of code. - */ -static DEFINE_SPINLOCK(rpc_sched_lock); - -/* * Disable the timer for a given RPC task. Should be called with * queue->lock and bh_disabled in order to avoid races within * rpc_run_timer(). @@ -994,20 +984,6 @@ void rpc_killall_tasks(struct rpc_clnt *clnt) spin_unlock(&clnt->cl_lock); } -void rpc_register_client(struct rpc_clnt *clnt) -{ - spin_lock(&rpc_sched_lock); - list_add(&clnt->cl_clients, &all_clients); - spin_unlock(&rpc_sched_lock); -} - -void rpc_unregister_client(struct rpc_clnt *clnt) -{ - spin_lock(&rpc_sched_lock); - list_del(&clnt->cl_clients); - spin_unlock(&rpc_sched_lock); -} - /* * Start up the rpciod process if it's not already running. */ @@ -1059,44 +1035,6 @@ rpciod_down(void) mutex_unlock(&rpciod_mutex); } -#ifdef RPC_DEBUG -void rpc_show_tasks(void) -{ - struct rpc_clnt *clnt; - struct rpc_task *t; - - spin_lock(&rpc_sched_lock); - if (list_empty(&all_clients)) - goto out; - printk("-pid- proc flgs status -client- -prog- --rqstp- -timeout " - "-rpcwait -action- ---ops--\n"); - list_for_each_entry(clnt, &all_clients, cl_clients) { - if (list_empty(&clnt->cl_tasks)) - continue; - spin_lock(&clnt->cl_lock); - list_for_each_entry(t, &clnt->cl_tasks, tk_task) { - const char *rpc_waitq = "none"; - - if (RPC_IS_QUEUED(t)) - rpc_waitq = rpc_qname(t->u.tk_wait.rpc_waitq); - - printk("%5u %04d %04x %6d %8p %6d %8p %8ld %8s %8p %8p\n", - t->tk_pid, - (t->tk_msg.rpc_proc ? t->tk_msg.rpc_proc->p_proc : -1), - t->tk_flags, t->tk_status, - t->tk_client, - (t->tk_client ? t->tk_client->cl_prog : 0), - t->tk_rqstp, t->tk_timeout, - rpc_waitq, - t->tk_action, t->tk_ops); - } - spin_unlock(&clnt->cl_lock); - } -out: - spin_unlock(&rpc_sched_lock); -} -#endif - void rpc_destroy_mempool(void) { -- cgit v1.1 From 6e5b70e9d1e712d8dad5514e0ab5240ac4b5fb57 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 12 Jun 2007 10:02:37 -0400 Subject: SUNRPC: clean up rpc_call_async/rpc_call_sync/rpc_run_task Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 115 ++++++++++++++++++++++++++++++++--------------------- net/sunrpc/sched.c | 23 ----------- 2 files changed, 69 insertions(+), 69 deletions(-) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 4f39ab1..76eef19 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -474,73 +474,96 @@ void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset) rpc_restore_sigmask(oldset); } -/* - * New rpc_call implementation +static +struct rpc_task *rpc_do_run_task(struct rpc_clnt *clnt, + struct rpc_message *msg, + int flags, + const struct rpc_call_ops *ops, + void *data) +{ + struct rpc_task *task, *ret; + sigset_t oldset; + + task = rpc_new_task(clnt, flags, ops, data); + if (task == NULL) { + rpc_release_calldata(ops, data); + return ERR_PTR(-ENOMEM); + } + + /* Mask signals on synchronous RPC calls and RPCSEC_GSS upcalls */ + rpc_task_sigmask(task, &oldset); + if (msg != NULL) { + rpc_call_setup(task, msg, 0); + if (task->tk_status != 0) { + ret = ERR_PTR(task->tk_status); + rpc_put_task(task); + goto out; + } + } + atomic_inc(&task->tk_count); + rpc_execute(task); + ret = task; +out: + rpc_restore_sigmask(&oldset); + return ret; +} + +/** + * rpc_call_sync - Perform a synchronous RPC call + * @clnt: pointer to RPC client + * @msg: RPC call parameters + * @flags: RPC call flags */ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) { struct rpc_task *task; - sigset_t oldset; - int status; + int status; BUG_ON(flags & RPC_TASK_ASYNC); - task = rpc_new_task(clnt, flags, &rpc_default_ops, NULL); - if (task == NULL) - return -ENOMEM; - - /* Mask signals on RPC calls _and_ GSS_AUTH upcalls */ - rpc_task_sigmask(task, &oldset); - - /* Set up the call info struct and execute the task */ - rpc_call_setup(task, msg, 0); - if (task->tk_status == 0) { - atomic_inc(&task->tk_count); - rpc_execute(task); - } + task = rpc_do_run_task(clnt, msg, flags, &rpc_default_ops, NULL); + if (IS_ERR(task)) + return PTR_ERR(task); status = task->tk_status; rpc_put_task(task); - rpc_restore_sigmask(&oldset); return status; } -/* - * New rpc_call implementation +/** + * rpc_call_async - Perform an asynchronous RPC call + * @clnt: pointer to RPC client + * @msg: RPC call parameters + * @flags: RPC call flags + * @ops: RPC call ops + * @data: user call data */ int rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags, const struct rpc_call_ops *tk_ops, void *data) { struct rpc_task *task; - sigset_t oldset; - int status; - - flags |= RPC_TASK_ASYNC; - - /* Create/initialize a new RPC task */ - status = -ENOMEM; - if (!(task = rpc_new_task(clnt, flags, tk_ops, data))) - goto out_release; - - /* Mask signals on GSS_AUTH upcalls */ - rpc_task_sigmask(task, &oldset); - - rpc_call_setup(task, msg, 0); - - /* Set up the call info struct and execute the task */ - status = task->tk_status; - if (status == 0) - rpc_execute(task); - else - rpc_put_task(task); - rpc_restore_sigmask(&oldset); - return status; -out_release: - rpc_release_calldata(tk_ops, data); - return status; + task = rpc_do_run_task(clnt, msg, flags|RPC_TASK_ASYNC, tk_ops, data); + if (IS_ERR(task)) + return PTR_ERR(task); + rpc_put_task(task); + return 0; } +/** + * rpc_run_task - Allocate a new RPC task, then run rpc_execute against it + * @clnt: pointer to RPC client + * @flags: RPC flags + * @ops: RPC call ops + * @data: user call data + */ +struct rpc_task *rpc_run_task(struct rpc_clnt *clnt, int flags, + const struct rpc_call_ops *tk_ops, + void *data) +{ + return rpc_do_run_task(clnt, NULL, flags, tk_ops, data); +} +EXPORT_SYMBOL(rpc_run_task); void rpc_call_setup(struct rpc_task *task, struct rpc_message *msg, int flags) diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index c0f8d25..2ac43c4 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -933,29 +933,6 @@ static void rpc_release_task(struct rpc_task *task) rpc_put_task(task); } -/** - * rpc_run_task - Allocate a new RPC task, then run rpc_execute against it - * @clnt: pointer to RPC client - * @flags: RPC flags - * @ops: RPC call ops - * @data: user call data - */ -struct rpc_task *rpc_run_task(struct rpc_clnt *clnt, int flags, - const struct rpc_call_ops *ops, - void *data) -{ - struct rpc_task *task; - task = rpc_new_task(clnt, flags, ops, data); - if (task == NULL) { - rpc_release_calldata(ops, data); - return ERR_PTR(-ENOMEM); - } - atomic_inc(&task->tk_count); - rpc_execute(task); - return task; -} -EXPORT_SYMBOL(rpc_run_task); - /* * Kill all tasks for the given client. * XXX: kill their descendants as well? -- cgit v1.1 From c1384c9c4c184543375b52a0997d06cd98145164 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 14 Jun 2007 18:00:42 -0400 Subject: SUNRPC: fix hang due to eventd deadlock... Brian Behlendorf writes: The root cause of the NFS hang we were observing appears to be a rare deadlock between the kernel provided usermodehelper API and the linux NFS client. The deadlock can arise because both of these services use the generic linux work queues. The usermodehelper API run the specified user application in the context of the work queue. And NFS submits both cleanup and reconnect work to the generic work queue for handling. Normally this is fine but a deadlock can result in the following situation. - NFS client is in a disconnected state - [events/0] runs a usermodehelper app with an NFS dependent operation, this triggers an NFS reconnect. - NFS reconnect happens to be submitted to [events/0] work queue. - Deadlock, the [events/0] work queue will never process the reconnect because it is blocked on the previous NFS dependent operation which will not complete.` The solution is simply to run reconnect requests on rpciod. Signed-off-by: Trond Myklebust --- net/sunrpc/xprt.c | 4 ++-- net/sunrpc/xprtsock.c | 17 +++++++---------- 2 files changed, 9 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 5b05b73..518acb7 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -127,7 +127,7 @@ static void xprt_clear_locked(struct rpc_xprt *xprt) clear_bit(XPRT_LOCKED, &xprt->state); smp_mb__after_clear_bit(); } else - schedule_work(&xprt->task_cleanup); + queue_work(rpciod_workqueue, &xprt->task_cleanup); } /* @@ -515,7 +515,7 @@ xprt_init_autodisconnect(unsigned long data) if (xprt_connecting(xprt)) xprt_release_write(xprt, NULL); else - schedule_work(&xprt->task_cleanup); + queue_work(rpciod_workqueue, &xprt->task_cleanup); return; out_abort: spin_unlock(&xprt->transport_lock); diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index cc33c58..ee6ad3b 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -653,8 +653,7 @@ static void xs_destroy(struct rpc_xprt *xprt) dprintk("RPC: xs_destroy xprt %p\n", xprt); - cancel_delayed_work(&transport->connect_worker); - flush_scheduled_work(); + cancel_rearming_delayed_work(&transport->connect_worker); xprt_disconnect(xprt); xs_close(xprt); @@ -1001,7 +1000,7 @@ static void xs_tcp_state_change(struct sock *sk) /* Try to schedule an autoclose RPC calls */ set_bit(XPRT_CLOSE_WAIT, &xprt->state); if (test_and_set_bit(XPRT_LOCKED, &xprt->state) == 0) - schedule_work(&xprt->task_cleanup); + queue_work(rpciod_workqueue, &xprt->task_cleanup); default: xprt_disconnect(xprt); } @@ -1410,18 +1409,16 @@ static void xs_connect(struct rpc_task *task) dprintk("RPC: xs_connect delayed xprt %p for %lu " "seconds\n", xprt, xprt->reestablish_timeout / HZ); - schedule_delayed_work(&transport->connect_worker, - xprt->reestablish_timeout); + queue_delayed_work(rpciod_workqueue, + &transport->connect_worker, + xprt->reestablish_timeout); xprt->reestablish_timeout <<= 1; if (xprt->reestablish_timeout > XS_TCP_MAX_REEST_TO) xprt->reestablish_timeout = XS_TCP_MAX_REEST_TO; } else { dprintk("RPC: xs_connect scheduled xprt %p\n", xprt); - schedule_delayed_work(&transport->connect_worker, 0); - - /* flush_scheduled_work can sleep... */ - if (!RPC_IS_ASYNC(task)) - flush_scheduled_work(); + queue_delayed_work(rpciod_workqueue, + &transport->connect_worker, 0); } } -- cgit v1.1 From 4a8c1344dccb848dbcf0edabc8b5c51a8ecf2808 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 7 Jun 2007 10:14:14 -0400 Subject: SUNRPC: Add a backpointer from the struct rpc_cred to the rpc_auth Cleans up an issue whereby rpcsec_gss uses the rpc_clnt->cl_auth. If we want to be able to add several rpc_auths to a single rpc_clnt, then this abuse must go. Signed-off-by: Trond Myklebust --- net/sunrpc/auth_gss/auth_gss.c | 3 ++- net/sunrpc/auth_null.c | 1 + net/sunrpc/auth_unix.c | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 4e4ccc5..e894e2f 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -386,7 +386,7 @@ static inline int gss_refresh_upcall(struct rpc_task *task) { struct rpc_cred *cred = task->tk_msg.rpc_cred; - struct gss_auth *gss_auth = container_of(task->tk_client->cl_auth, + struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); @@ -741,6 +741,7 @@ gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) * fail to flag the credential as RPCAUTH_CRED_UPTODATE. */ cred->gc_flags = 0; + cred->gc_base.cr_auth = auth; cred->gc_base.cr_ops = &gss_credops; cred->gc_base.cr_flags = RPCAUTH_CRED_NEW; cred->gc_service = gss_auth->service; diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c index 3df9fcc..890bd9b 100644 --- a/net/sunrpc/auth_null.c +++ b/net/sunrpc/auth_null.c @@ -133,6 +133,7 @@ struct rpc_credops null_credops = { static struct rpc_cred null_cred = { + .cr_auth = &null_auth, .cr_ops = &null_credops, .cr_count = ATOMIC_INIT(1), .cr_flags = RPCAUTH_CRED_UPTODATE, diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 4e7733a..82300b8 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -92,6 +92,7 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) if (i < NFS_NGROUPS) cred->uc_gids[i] = NOGROUP; } + cred->uc_base.cr_auth = &unix_auth; cred->uc_base.cr_ops = &unix_credops; return (struct rpc_cred *) cred; -- cgit v1.1 From b185f835e243e654047ae85f42346827d3894171 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 7 Jun 2007 10:14:14 -0400 Subject: SUNRPC: Remove the gss_auth spinlock We're just as well off using the inode spinlock instead. Signed-off-by: Trond Myklebust --- net/sunrpc/auth_gss/auth_gss.c | 47 ++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 22 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index e894e2f..653d712 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -88,7 +88,6 @@ struct gss_auth { struct list_head upcalls; struct rpc_clnt *client; struct dentry *dentry; - spinlock_t lock; }; static void gss_destroy_ctx(struct gss_cl_ctx *); @@ -290,16 +289,17 @@ __gss_find_upcall(struct gss_auth *gss_auth, uid_t uid) static inline struct gss_upcall_msg * gss_add_msg(struct gss_auth *gss_auth, struct gss_upcall_msg *gss_msg) { + struct inode *inode = gss_auth->dentry->d_inode; struct gss_upcall_msg *old; - spin_lock(&gss_auth->lock); + spin_lock(&inode->i_lock); old = __gss_find_upcall(gss_auth, gss_msg->uid); if (old == NULL) { atomic_inc(&gss_msg->count); list_add(&gss_msg->list, &gss_auth->upcalls); } else gss_msg = old; - spin_unlock(&gss_auth->lock); + spin_unlock(&inode->i_lock); return gss_msg; } @@ -318,10 +318,11 @@ static void gss_unhash_msg(struct gss_upcall_msg *gss_msg) { struct gss_auth *gss_auth = gss_msg->auth; + struct inode *inode = gss_auth->dentry->d_inode; - spin_lock(&gss_auth->lock); + spin_lock(&inode->i_lock); __gss_unhash_msg(gss_msg); - spin_unlock(&gss_auth->lock); + spin_unlock(&inode->i_lock); } static void @@ -330,16 +331,16 @@ gss_upcall_callback(struct rpc_task *task) struct gss_cred *gss_cred = container_of(task->tk_msg.rpc_cred, struct gss_cred, gc_base); struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall; + struct inode *inode = gss_msg->auth->dentry->d_inode; - BUG_ON(gss_msg == NULL); if (gss_msg->ctx) gss_cred_set_ctx(task->tk_msg.rpc_cred, gss_get_ctx(gss_msg->ctx)); else task->tk_status = gss_msg->msg.errno; - spin_lock(&gss_msg->auth->lock); + spin_lock(&inode->i_lock); gss_cred->gc_upcall = NULL; rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); - spin_unlock(&gss_msg->auth->lock); + spin_unlock(&inode->i_lock); gss_release_msg(gss_msg); } @@ -391,6 +392,7 @@ gss_refresh_upcall(struct rpc_task *task) struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_upcall_msg *gss_msg; + struct inode *inode = gss_auth->dentry->d_inode; int err = 0; dprintk("RPC: %5u gss_refresh_upcall for uid %u\n", task->tk_pid, @@ -400,7 +402,7 @@ gss_refresh_upcall(struct rpc_task *task) err = PTR_ERR(gss_msg); goto out; } - spin_lock(&gss_auth->lock); + spin_lock(&inode->i_lock); if (gss_cred->gc_upcall != NULL) rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL, NULL); else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) { @@ -411,7 +413,7 @@ gss_refresh_upcall(struct rpc_task *task) rpc_sleep_on(&gss_msg->rpc_waitqueue, task, gss_upcall_callback, NULL); } else err = gss_msg->msg.errno; - spin_unlock(&gss_auth->lock); + spin_unlock(&inode->i_lock); gss_release_msg(gss_msg); out: dprintk("RPC: %5u gss_refresh_upcall for uid %u result %d\n", @@ -422,6 +424,7 @@ out: static inline int gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) { + struct inode *inode = gss_auth->dentry->d_inode; struct rpc_cred *cred = &gss_cred->gc_base; struct gss_upcall_msg *gss_msg; DEFINE_WAIT(wait); @@ -435,12 +438,12 @@ gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) } for (;;) { prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_INTERRUPTIBLE); - spin_lock(&gss_auth->lock); + spin_lock(&inode->i_lock); if (gss_msg->ctx != NULL || gss_msg->msg.errno < 0) { - spin_unlock(&gss_auth->lock); + spin_unlock(&inode->i_lock); break; } - spin_unlock(&gss_auth->lock); + spin_unlock(&inode->i_lock); if (signalled()) { err = -ERESTARTSYS; goto out_intr; @@ -492,6 +495,7 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) struct gss_auth *gss_auth; struct rpc_cred *cred; struct gss_upcall_msg *gss_msg; + struct inode *inode = filp->f_path.dentry->d_inode; struct gss_cl_ctx *ctx; uid_t uid; int err = -EFBIG; @@ -503,7 +507,7 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) if (!buf) goto out; - clnt = RPC_I(filp->f_path.dentry->d_inode)->private; + clnt = RPC_I(inode)->private; err = -EFAULT; if (copy_from_user(buf, src, mlen)) goto err; @@ -527,18 +531,18 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) if (err != -EACCES) goto err_put_ctx; } - spin_lock(&gss_auth->lock); + spin_lock(&inode->i_lock); gss_msg = __gss_find_upcall(gss_auth, uid); if (gss_msg) { if (err == 0 && gss_msg->ctx == NULL) gss_msg->ctx = gss_get_ctx(ctx); gss_msg->msg.errno = err; __gss_unhash_msg(gss_msg); - spin_unlock(&gss_auth->lock); + spin_unlock(&inode->i_lock); gss_release_msg(gss_msg); } else { struct auth_cred acred = { .uid = uid }; - spin_unlock(&gss_auth->lock); + spin_unlock(&inode->i_lock); cred = rpcauth_lookup_credcache(clnt->cl_auth, &acred, RPCAUTH_LOOKUP_NEW); if (IS_ERR(cred)) { err = PTR_ERR(cred); @@ -570,7 +574,7 @@ gss_pipe_release(struct inode *inode) clnt = rpci->private; auth = clnt->cl_auth; gss_auth = container_of(auth, struct gss_auth, rpc_auth); - spin_lock(&gss_auth->lock); + spin_lock(&inode->i_lock); while (!list_empty(&gss_auth->upcalls)) { struct gss_upcall_msg *gss_msg; @@ -579,11 +583,11 @@ gss_pipe_release(struct inode *inode) gss_msg->msg.errno = -EPIPE; atomic_inc(&gss_msg->count); __gss_unhash_msg(gss_msg); - spin_unlock(&gss_auth->lock); + spin_unlock(&inode->i_lock); gss_release_msg(gss_msg); - spin_lock(&gss_auth->lock); + spin_lock(&inode->i_lock); } - spin_unlock(&gss_auth->lock); + spin_unlock(&inode->i_lock); } static void @@ -638,7 +642,6 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) if (gss_auth->service == 0) goto err_put_mech; INIT_LIST_HEAD(&gss_auth->upcalls); - spin_lock_init(&gss_auth->lock); auth = &gss_auth->rpc_auth; auth->au_cslack = GSS_CRED_SLACK >> 2; auth->au_rslack = GSS_VERF_SLACK >> 2; -- cgit v1.1 From 3b68aaeaf54065e5c44583a1d33ffb7793953ba4 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 7 Jun 2007 10:14:15 -0400 Subject: SUNRPC: Always match an upcall message in gss_pipe_downcall() It used to be possible for an rpc.gssd daemon to stuff the RPC credential cache for any rpc client simply by creating RPCSEC_GSS contexts and then doing downcalls. In practice, no daemons ever made use of this feature. Remove this feature now, since it will be impossible to figure out which mechanism a given context actually matches if we enable more than one gss mechanism to use the same upcall pipe. Signed-off-by: Trond Myklebust --- net/sunrpc/auth_gss/auth_gss.c | 58 ++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 31 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 653d712..e407a35 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -306,8 +306,6 @@ gss_add_msg(struct gss_auth *gss_auth, struct gss_upcall_msg *gss_msg) static void __gss_unhash_msg(struct gss_upcall_msg *gss_msg) { - if (list_empty(&gss_msg->list)) - return; list_del_init(&gss_msg->list); rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); wake_up_all(&gss_msg->waitqueue); @@ -320,8 +318,11 @@ gss_unhash_msg(struct gss_upcall_msg *gss_msg) struct gss_auth *gss_auth = gss_msg->auth; struct inode *inode = gss_auth->dentry->d_inode; + if (list_empty(&gss_msg->list)) + return; spin_lock(&inode->i_lock); - __gss_unhash_msg(gss_msg); + if (!list_empty(&gss_msg->list)) + __gss_unhash_msg(gss_msg); spin_unlock(&inode->i_lock); } @@ -493,12 +494,11 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) void *buf; struct rpc_clnt *clnt; struct gss_auth *gss_auth; - struct rpc_cred *cred; struct gss_upcall_msg *gss_msg; struct inode *inode = filp->f_path.dentry->d_inode; struct gss_cl_ctx *ctx; uid_t uid; - int err = -EFBIG; + ssize_t err = -EFBIG; if (mlen > MSG_BUF_MAXSIZE) goto out; @@ -523,43 +523,39 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) ctx = gss_alloc_context(); if (ctx == NULL) goto err; - err = 0; + + err = -ENOENT; + /* Find a matching upcall */ gss_auth = container_of(clnt->cl_auth, struct gss_auth, rpc_auth); + spin_lock(&inode->i_lock); + gss_msg = __gss_find_upcall(gss_auth, uid); + if (gss_msg == NULL) { + spin_unlock(&inode->i_lock); + goto err_put_ctx; + } + list_del_init(&gss_msg->list); + spin_unlock(&inode->i_lock); + p = gss_fill_context(p, end, ctx, gss_auth->mech); if (IS_ERR(p)) { err = PTR_ERR(p); - if (err != -EACCES) - goto err_put_ctx; + gss_msg->msg.errno = (err == -EACCES) ? -EACCES : -EAGAIN; + goto err_release_msg; } + gss_msg->ctx = gss_get_ctx(ctx); + err = mlen; + +err_release_msg: spin_lock(&inode->i_lock); - gss_msg = __gss_find_upcall(gss_auth, uid); - if (gss_msg) { - if (err == 0 && gss_msg->ctx == NULL) - gss_msg->ctx = gss_get_ctx(ctx); - gss_msg->msg.errno = err; - __gss_unhash_msg(gss_msg); - spin_unlock(&inode->i_lock); - gss_release_msg(gss_msg); - } else { - struct auth_cred acred = { .uid = uid }; - spin_unlock(&inode->i_lock); - cred = rpcauth_lookup_credcache(clnt->cl_auth, &acred, RPCAUTH_LOOKUP_NEW); - if (IS_ERR(cred)) { - err = PTR_ERR(cred); - goto err_put_ctx; - } - gss_cred_set_ctx(cred, gss_get_ctx(ctx)); - } - gss_put_ctx(ctx); - kfree(buf); - dprintk("RPC: gss_pipe_downcall returning length %Zu\n", mlen); - return mlen; + __gss_unhash_msg(gss_msg); + spin_unlock(&inode->i_lock); + gss_release_msg(gss_msg); err_put_ctx: gss_put_ctx(ctx); err: kfree(buf); out: - dprintk("RPC: gss_pipe_downcall returning %d\n", err); + dprintk("RPC: gss_pipe_downcall returning %Zd\n", err); return err; } -- cgit v1.1 From 6e84c7b66a0aa0be16a7728d1e687c57978dac2c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 7 Jun 2007 15:31:36 -0400 Subject: SUNRPC: Add a downcall queue to struct rpc_inode Currently, the downcall queue is tied to the struct gss_auth, which means that different RPCSEC_GSS pseudoflavours must use different upcall pipes. Add a list to struct rpc_inode that can be used instead. Signed-off-by: Trond Myklebust --- net/sunrpc/auth_gss/auth_gss.c | 29 ++++++++++------------------- net/sunrpc/rpc_pipe.c | 1 + 2 files changed, 11 insertions(+), 19 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index e407a35..50809086 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -85,7 +85,6 @@ struct gss_auth { struct rpc_auth rpc_auth; struct gss_api_mech *mech; enum rpc_gss_svc service; - struct list_head upcalls; struct rpc_clnt *client; struct dentry *dentry; }; @@ -268,10 +267,10 @@ gss_release_msg(struct gss_upcall_msg *gss_msg) } static struct gss_upcall_msg * -__gss_find_upcall(struct gss_auth *gss_auth, uid_t uid) +__gss_find_upcall(struct rpc_inode *rpci, uid_t uid) { struct gss_upcall_msg *pos; - list_for_each_entry(pos, &gss_auth->upcalls, list) { + list_for_each_entry(pos, &rpci->in_downcall, list) { if (pos->uid != uid) continue; atomic_inc(&pos->count); @@ -290,13 +289,14 @@ static inline struct gss_upcall_msg * gss_add_msg(struct gss_auth *gss_auth, struct gss_upcall_msg *gss_msg) { struct inode *inode = gss_auth->dentry->d_inode; + struct rpc_inode *rpci = RPC_I(inode); struct gss_upcall_msg *old; spin_lock(&inode->i_lock); - old = __gss_find_upcall(gss_auth, gss_msg->uid); + old = __gss_find_upcall(rpci, gss_msg->uid); if (old == NULL) { atomic_inc(&gss_msg->count); - list_add(&gss_msg->list, &gss_auth->upcalls); + list_add(&gss_msg->list, &rpci->in_downcall); } else gss_msg = old; spin_unlock(&inode->i_lock); @@ -493,7 +493,6 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) const void *p, *end; void *buf; struct rpc_clnt *clnt; - struct gss_auth *gss_auth; struct gss_upcall_msg *gss_msg; struct inode *inode = filp->f_path.dentry->d_inode; struct gss_cl_ctx *ctx; @@ -526,9 +525,8 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) err = -ENOENT; /* Find a matching upcall */ - gss_auth = container_of(clnt->cl_auth, struct gss_auth, rpc_auth); spin_lock(&inode->i_lock); - gss_msg = __gss_find_upcall(gss_auth, uid); + gss_msg = __gss_find_upcall(RPC_I(inode), uid); if (gss_msg == NULL) { spin_unlock(&inode->i_lock); goto err_put_ctx; @@ -536,7 +534,7 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) list_del_init(&gss_msg->list); spin_unlock(&inode->i_lock); - p = gss_fill_context(p, end, ctx, gss_auth->mech); + p = gss_fill_context(p, end, ctx, gss_msg->auth->mech); if (IS_ERR(p)) { err = PTR_ERR(p); gss_msg->msg.errno = (err == -EACCES) ? -EACCES : -EAGAIN; @@ -563,18 +561,12 @@ static void gss_pipe_release(struct inode *inode) { struct rpc_inode *rpci = RPC_I(inode); - struct rpc_clnt *clnt; - struct rpc_auth *auth; - struct gss_auth *gss_auth; + struct gss_upcall_msg *gss_msg; - clnt = rpci->private; - auth = clnt->cl_auth; - gss_auth = container_of(auth, struct gss_auth, rpc_auth); spin_lock(&inode->i_lock); - while (!list_empty(&gss_auth->upcalls)) { - struct gss_upcall_msg *gss_msg; + while (!list_empty(&rpci->in_downcall)) { - gss_msg = list_entry(gss_auth->upcalls.next, + gss_msg = list_entry(rpci->in_downcall.next, struct gss_upcall_msg, list); gss_msg->msg.errno = -EPIPE; atomic_inc(&gss_msg->count); @@ -637,7 +629,6 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor); if (gss_auth->service == 0) goto err_put_mech; - INIT_LIST_HEAD(&gss_auth->upcalls); auth = &gss_auth->rpc_auth; auth->au_cslack = GSS_CRED_SLACK >> 2; auth->au_rslack = GSS_VERF_SLACK >> 2; diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 826190d..2320f1e 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -833,6 +833,7 @@ init_once(void * foo, struct kmem_cache * cachep, unsigned long flags) rpci->nreaders = 0; rpci->nwriters = 0; INIT_LIST_HEAD(&rpci->in_upcall); + INIT_LIST_HEAD(&rpci->in_downcall); INIT_LIST_HEAD(&rpci->pipe); rpci->pipelen = 0; init_waitqueue_head(&rpci->waitq); -- cgit v1.1 From 34f308960818e514fadd9365cb5b14cca319320b Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 7 Jun 2007 18:28:02 -0400 Subject: SUNRPC: Enable non-exclusive create in rpc_mkpipe() Signed-off-by: Trond Myklebust --- net/sunrpc/rpc_pipe.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 2320f1e..ebcb805 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -621,7 +621,7 @@ __rpc_rmdir(struct inode *dir, struct dentry *dentry) } static struct dentry * -rpc_lookup_create(struct dentry *parent, const char *name, int len) +rpc_lookup_create(struct dentry *parent, const char *name, int len, int exclusive) { struct inode *dir = parent->d_inode; struct dentry *dentry; @@ -630,7 +630,7 @@ rpc_lookup_create(struct dentry *parent, const char *name, int len) dentry = lookup_one_len(name, parent, len); if (IS_ERR(dentry)) goto out_err; - if (dentry->d_inode) { + if (dentry->d_inode && exclusive) { dput(dentry); dentry = ERR_PTR(-EEXIST); goto out_err; @@ -649,7 +649,7 @@ rpc_lookup_negative(char *path, struct nameidata *nd) if ((error = rpc_lookup_parent(path, nd)) != 0) return ERR_PTR(error); - dentry = rpc_lookup_create(nd->dentry, nd->last.name, nd->last.len); + dentry = rpc_lookup_create(nd->dentry, nd->last.name, nd->last.len, 1); if (IS_ERR(dentry)) rpc_release_path(nd); return dentry; @@ -716,10 +716,20 @@ rpc_mkpipe(struct dentry *parent, const char *name, void *private, struct rpc_pi struct inode *dir, *inode; struct rpc_inode *rpci; - dentry = rpc_lookup_create(parent, name, strlen(name)); + dentry = rpc_lookup_create(parent, name, strlen(name), 0); if (IS_ERR(dentry)) return dentry; dir = parent->d_inode; + if (dentry->d_inode) { + rpci = RPC_I(dentry->d_inode); + if (rpci->private != private || + rpci->ops != ops || + rpci->flags != flags) { + dput (dentry); + dentry = ERR_PTR(-EBUSY); + } + goto out; + } inode = rpc_get_inode(dir->i_sb, S_IFIFO | S_IRUSR | S_IWUSR); if (!inode) goto err_dput; -- cgit v1.1 From 62e1761ceff5dbb78c4b4b9486d8ca9fed11b660 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 8 Jun 2007 14:14:46 -0400 Subject: SUNRPC: Clean up rpc_pipefs. Add a dentry_ops with a d_delete() method in order to ensure that dentries are removed as soon as the last reference is gone. Clean up rpc_depopulate() so that it only removes files that were created via rpc_populate(). Signed-off-by: Trond Myklebust --- net/sunrpc/rpc_pipe.c | 59 ++++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 27 deletions(-) (limited to 'net') diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index ebcb805..e5fd796 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -448,6 +448,15 @@ void rpc_put_mount(void) simple_release_fs(&rpc_mount, &rpc_mount_count); } +static int rpc_delete_dentry(struct dentry *dentry) +{ + return 1; +} + +static struct dentry_operations rpc_dentry_operations = { + .d_delete = rpc_delete_dentry, +}; + static int rpc_lookup_parent(char *path, struct nameidata *nd) { @@ -506,7 +515,7 @@ rpc_get_inode(struct super_block *sb, int mode) * FIXME: This probably has races. */ static void -rpc_depopulate(struct dentry *parent) +rpc_depopulate(struct dentry *parent, int start, int eof) { struct inode *dir = parent->d_inode; struct list_head *pos, *next; @@ -518,6 +527,10 @@ repeat: spin_lock(&dcache_lock); list_for_each_safe(pos, next, &parent->d_subdirs) { dentry = list_entry(pos, struct dentry, d_u.d_child); + if (!dentry->d_inode || + dentry->d_inode->i_ino < start || + dentry->d_inode->i_ino >= eof) + continue; spin_lock(&dentry->d_lock); if (!d_unhashed(dentry)) { dget_locked(dentry); @@ -533,11 +546,11 @@ repeat: if (n) { do { dentry = dvec[--n]; - if (dentry->d_inode) { - rpc_close_pipes(dentry->d_inode); + if (S_ISREG(dentry->d_inode->i_mode)) simple_unlink(dir, dentry); - } - inode_dir_notify(dir, DN_DELETE); + else if (S_ISDIR(dentry->d_inode->i_mode)) + simple_rmdir(dir, dentry); + d_delete(dentry); dput(dentry); } while (n); goto repeat; @@ -560,6 +573,7 @@ rpc_populate(struct dentry *parent, dentry = d_alloc_name(parent, files[i].name); if (!dentry) goto out_bad; + dentry->d_op = &rpc_dentry_operations; mode = files[i].mode; inode = rpc_get_inode(dir->i_sb, mode); if (!inode) { @@ -607,17 +621,10 @@ static int __rpc_rmdir(struct inode *dir, struct dentry *dentry) { int error; - - shrink_dcache_parent(dentry); - if (d_unhashed(dentry)) - return 0; - if ((error = simple_rmdir(dir, dentry)) != 0) - return error; - if (!error) { - inode_dir_notify(dir, DN_DELETE); - d_drop(dentry); - } - return 0; + error = simple_rmdir(dir, dentry); + if (!error) + d_delete(dentry); + return error; } static struct dentry * @@ -630,7 +637,9 @@ rpc_lookup_create(struct dentry *parent, const char *name, int len, int exclusiv dentry = lookup_one_len(name, parent, len); if (IS_ERR(dentry)) goto out_err; - if (dentry->d_inode && exclusive) { + if (!dentry->d_inode) + dentry->d_op = &rpc_dentry_operations; + else if (exclusive) { dput(dentry); dentry = ERR_PTR(-EEXIST); goto out_err; @@ -681,7 +690,7 @@ out: rpc_release_path(&nd); return dentry; err_depopulate: - rpc_depopulate(dentry); + rpc_depopulate(dentry, RPCAUTH_info, RPCAUTH_EOF); __rpc_rmdir(dir, dentry); err_dput: dput(dentry); @@ -701,7 +710,7 @@ rpc_rmdir(struct dentry *dentry) parent = dget_parent(dentry); dir = parent->d_inode; mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); - rpc_depopulate(dentry); + rpc_depopulate(dentry, RPCAUTH_info, RPCAUTH_EOF); error = __rpc_rmdir(dir, dentry); dput(dentry); mutex_unlock(&dir->i_mutex); @@ -764,14 +773,10 @@ rpc_unlink(struct dentry *dentry) parent = dget_parent(dentry); dir = parent->d_inode; mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); - if (!d_unhashed(dentry)) { - d_drop(dentry); - if (dentry->d_inode) { - rpc_close_pipes(dentry->d_inode); - error = simple_unlink(dir, dentry); - } - inode_dir_notify(dir, DN_DELETE); - } + rpc_close_pipes(dentry->d_inode); + error = simple_unlink(dir, dentry); + if (!error) + d_delete(dentry); dput(dentry); mutex_unlock(&dir->i_mutex); dput(parent); -- cgit v1.1 From 03a1256f06cf1f58e33971fb4a524479e75c200e Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 8 Jun 2007 14:14:53 -0400 Subject: SUNRPC: Add a field to track the number of kernel users of an rpc_pipe This allows us to correctly deduce when we need to remove the pipe. Signed-off-by: Trond Myklebust --- net/sunrpc/rpc_pipe.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index e5fd796..e787b6a 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -737,6 +737,7 @@ rpc_mkpipe(struct dentry *parent, const char *name, void *private, struct rpc_pi dput (dentry); dentry = ERR_PTR(-EBUSY); } + rpci->nkern_readwriters++; goto out; } inode = rpc_get_inode(dir->i_sb, S_IFIFO | S_IRUSR | S_IWUSR); @@ -749,6 +750,7 @@ rpc_mkpipe(struct dentry *parent, const char *name, void *private, struct rpc_pi rpci->private = private; rpci->flags = flags; rpci->ops = ops; + rpci->nkern_readwriters = 1; inode_dir_notify(dir, DN_CREATE); dget(dentry); out: @@ -773,10 +775,12 @@ rpc_unlink(struct dentry *dentry) parent = dget_parent(dentry); dir = parent->d_inode; mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); - rpc_close_pipes(dentry->d_inode); - error = simple_unlink(dir, dentry); - if (!error) - d_delete(dentry); + if (--RPC_I(dentry->d_inode)->nkern_readwriters == 0) { + rpc_close_pipes(dentry->d_inode); + error = simple_unlink(dir, dentry); + if (!error) + d_delete(dentry); + } dput(dentry); mutex_unlock(&dir->i_mutex); dput(parent); -- cgit v1.1 From 3ab9bb7243489f9db3abf3d05521ddfc6b184c0a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 9 Jun 2007 15:41:42 -0400 Subject: SUNRPC: Fix a memory leak in the auth credcache code The leak only affects the RPCSEC_GSS caches, since they are the only ones that are dynamically allocated... Rename the existing rpcauth_free_credcache() to rpcauth_clear_credcache() in order to better describe its role, then add a new function rpcauth_destroy_credcache() that actually frees the cache in addition to clearing it out. Also move the call to destroy the credcache in gss_destroy() to come before the rpc upcall pipe is unlinked. Signed-off-by: Trond Myklebust --- net/sunrpc/auth.c | 18 ++++++++++++++++-- net/sunrpc/auth_gss/auth_gss.c | 3 ++- net/sunrpc/auth_unix.c | 2 +- net/sunrpc/sunrpc_syms.c | 2 +- 4 files changed, 20 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 9527f2b..f6b6c81 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -137,9 +137,8 @@ void rpcauth_destroy_credlist(struct hlist_head *head) * that are not referenced. */ void -rpcauth_free_credcache(struct rpc_auth *auth) +rpcauth_clear_credcache(struct rpc_cred_cache *cache) { - struct rpc_cred_cache *cache = auth->au_credcache; HLIST_HEAD(free); struct hlist_node *pos, *next; struct rpc_cred *cred; @@ -157,6 +156,21 @@ rpcauth_free_credcache(struct rpc_auth *auth) rpcauth_destroy_credlist(&free); } +/* + * Destroy the RPC credential cache + */ +void +rpcauth_destroy_credcache(struct rpc_auth *auth) +{ + struct rpc_cred_cache *cache = auth->au_credcache; + + if (cache) { + auth->au_credcache = NULL; + rpcauth_clear_credcache(cache); + kfree(cache); + } +} + static void rpcauth_prune_expired(struct rpc_auth *auth, struct rpc_cred *cred, struct hlist_head *free) { diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 50809086..8b4c02f 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -665,12 +665,13 @@ gss_destroy(struct rpc_auth *auth) dprintk("RPC: destroying GSS authenticator %p flavor %d\n", auth, auth->au_flavor); + rpcauth_destroy_credcache(auth); + gss_auth = container_of(auth, struct gss_auth, rpc_auth); rpc_unlink(gss_auth->dentry); gss_auth->dentry = NULL; gss_mech_put(gss_auth->mech); - rpcauth_free_credcache(auth); kfree(gss_auth); module_put(THIS_MODULE); } diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 82300b8..5622783 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -50,7 +50,7 @@ static void unx_destroy(struct rpc_auth *auth) { dprintk("RPC: destroying UNIX authenticator %p\n", auth); - rpcauth_free_credcache(auth); + rpcauth_clear_credcache(auth->au_credcache); } /* diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index b99b11b..3e19e7a 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -57,7 +57,7 @@ EXPORT_SYMBOL(rpcauth_unregister); EXPORT_SYMBOL(rpcauth_create); EXPORT_SYMBOL(rpcauth_lookupcred); EXPORT_SYMBOL(rpcauth_lookup_credcache); -EXPORT_SYMBOL(rpcauth_free_credcache); +EXPORT_SYMBOL(rpcauth_destroy_credcache); EXPORT_SYMBOL(rpcauth_init_credcache); EXPORT_SYMBOL(put_rpccred); -- cgit v1.1 From 5c9cfc828ae34e19dabbdb9f2861b8c920454047 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sun, 24 Jun 2007 15:24:29 -0400 Subject: SUNRPC: Fix a typo in unx_create() We want to set the unix_cred_cache.nextgc on the first call to unx_create(), which should be when unix_auth.au_count === 1 Signed-off-by: Trond Myklebust --- net/sunrpc/auth_unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 5622783..e54782e 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -41,7 +41,7 @@ unx_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) { dprintk("RPC: creating UNIX authenticator for client %p\n", clnt); - if (atomic_inc_return(&unix_auth.au_count) == 0) + if (atomic_inc_return(&unix_auth.au_count) == 1) unix_cred_cache.nextgc = jiffies + (unix_cred_cache.expire >> 1); return &unix_auth; } -- cgit v1.1 From 07a2bf1da4765d987ffd1d8045e92ba032e0ad78 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 9 Jun 2007 15:42:01 -0400 Subject: SUNRPC: Fix a memory leak in gss_create() Fix a memory leak in gss_create() whereby the rpc credcache was not being freed if the rpc_mkpipe() call failed. Signed-off-by: Trond Myklebust --- net/sunrpc/auth_gss/auth_gss.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 8b4c02f..459dc9b 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -636,10 +636,6 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) auth->au_flavor = flavor; atomic_set(&auth->au_count, 1); - err = rpcauth_init_credcache(auth, GSS_CRED_EXPIRE); - if (err) - goto err_put_mech; - gss_auth->dentry = rpc_mkpipe(clnt->cl_dentry, gss_auth->mech->gm_name, clnt, &gss_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN); if (IS_ERR(gss_auth->dentry)) { @@ -647,7 +643,13 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) goto err_put_mech; } + err = rpcauth_init_credcache(auth, GSS_CRED_EXPIRE); + if (err) + goto err_unlink_pipe; + return auth; +err_unlink_pipe: + rpc_unlink(gss_auth->dentry); err_put_mech: gss_mech_put(gss_auth->mech); err_free: -- cgit v1.1 From fc1b356f566fe05929ec2a88ce2c7b15f8b6279f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 9 Jun 2007 16:15:46 -0400 Subject: SUNRPC: Fix races in rpcauth_create See the FIXME: auth_flavors[] really needs a lock and module refcounting. Signed-off-by: Trond Myklebust --- net/sunrpc/auth.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index f6b6c81..584f243 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -18,6 +18,7 @@ # define RPCDBG_FACILITY RPCDBG_AUTH #endif +static DEFINE_SPINLOCK(rpc_authflavor_lock); static struct rpc_authops * auth_flavors[RPC_AUTH_MAXFLAVOR] = { &authnull_ops, /* AUTH_NULL */ &authunix_ops, /* AUTH_UNIX */ @@ -35,26 +36,34 @@ int rpcauth_register(struct rpc_authops *ops) { rpc_authflavor_t flavor; + int ret = -EPERM; if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR) return -EINVAL; - if (auth_flavors[flavor] != NULL) - return -EPERM; /* what else? */ - auth_flavors[flavor] = ops; - return 0; + spin_lock(&rpc_authflavor_lock); + if (auth_flavors[flavor] == NULL) { + auth_flavors[flavor] = ops; + ret = 0; + } + spin_unlock(&rpc_authflavor_lock); + return ret; } int rpcauth_unregister(struct rpc_authops *ops) { rpc_authflavor_t flavor; + int ret = -EPERM; if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR) return -EINVAL; - if (auth_flavors[flavor] != ops) - return -EPERM; /* what else? */ - auth_flavors[flavor] = NULL; - return 0; + spin_lock(&rpc_authflavor_lock); + if (auth_flavors[flavor] == ops) { + auth_flavors[flavor] = NULL; + ret = 0; + } + spin_unlock(&rpc_authflavor_lock); + return ret; } struct rpc_auth * @@ -68,15 +77,19 @@ rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt) if (flavor >= RPC_AUTH_MAXFLAVOR) goto out; - /* FIXME - auth_flavors[] really needs an rw lock, - * and module refcounting. */ #ifdef CONFIG_KMOD if ((ops = auth_flavors[flavor]) == NULL) request_module("rpc-auth-%u", flavor); #endif - if ((ops = auth_flavors[flavor]) == NULL) + spin_lock(&rpc_authflavor_lock); + ops = auth_flavors[flavor]; + if (ops == NULL || !try_module_get(ops->owner)) { + spin_unlock(&rpc_authflavor_lock); goto out; + } + spin_unlock(&rpc_authflavor_lock); auth = ops->create(clnt, pseudoflavor); + module_put(ops->owner); if (IS_ERR(auth)) return auth; if (clnt->cl_auth) -- cgit v1.1 From 64c91a1f1c8bc4295fd6b90df8adf911a7dd64f4 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 23 Jun 2007 10:17:16 -0400 Subject: SUNRPC: Make rpc_ping() static Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 76eef19..4e91f31 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -72,6 +72,8 @@ static void call_connect_status(struct rpc_task *task); static __be32 * call_header(struct rpc_task *task); static __be32 * call_verify(struct rpc_task *task); +static int rpc_ping(struct rpc_clnt *clnt, int flags); + static void rpc_register_client(struct rpc_clnt *clnt) { spin_lock(&rpc_client_lock); @@ -1441,7 +1443,7 @@ static struct rpc_procinfo rpcproc_null = { .p_decode = rpcproc_decode_null, }; -int rpc_ping(struct rpc_clnt *clnt, int flags) +static int rpc_ping(struct rpc_clnt *clnt, int flags) { struct rpc_message msg = { .rpc_proc = &rpcproc_null, -- cgit v1.1 From 5e1550d6a2c2dd33ff0ca5febefd8e9c65c6ca1e Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 23 Jun 2007 10:17:16 -0400 Subject: SUNRPC: Add the helper function 'rpc_call_null()' Does a NULL RPC call and returns a pointer to the resulting rpc_task. The call may be either synchronous or asynchronous. Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 4e91f31..5a28ffa 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1455,6 +1455,16 @@ static int rpc_ping(struct rpc_clnt *clnt, int flags) return err; } +struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred, int flags) +{ + struct rpc_message msg = { + .rpc_proc = &rpcproc_null, + .rpc_cred = cred, + }; + return rpc_do_run_task(clnt, &msg, flags, &rpc_default_ops, NULL); +} +EXPORT_SYMBOL(rpc_call_null); + #ifdef RPC_DEBUG void rpc_show_tasks(void) { -- cgit v1.1 From de7a8ce38aea529876db3890b61947bc4bc004da Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 23 Jun 2007 10:46:47 -0400 Subject: SUNRPC: Rename rpcauth_destroy() to rpcauth_release() Signed-off-by: Trond Myklebust --- net/sunrpc/auth.c | 4 ++-- net/sunrpc/clnt.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 584f243..1686dc7 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -93,7 +93,7 @@ rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt) if (IS_ERR(auth)) return auth; if (clnt->cl_auth) - rpcauth_destroy(clnt->cl_auth); + rpcauth_release(clnt->cl_auth); clnt->cl_auth = auth; out: @@ -101,7 +101,7 @@ out: } void -rpcauth_destroy(struct rpc_auth *auth) +rpcauth_release(struct rpc_auth *auth) { if (!atomic_dec_and_test(&auth->au_count)) return; diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 5a28ffa..98df44e 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -353,7 +353,7 @@ rpc_free_client(struct kref *kref) dprintk("RPC: destroying %s client for %s\n", clnt->cl_protname, clnt->cl_server); if (clnt->cl_auth) { - rpcauth_destroy(clnt->cl_auth); + rpcauth_release(clnt->cl_auth); clnt->cl_auth = NULL; } if (!IS_ERR(clnt->cl_dentry)) { -- cgit v1.1 From f1c0a8615090359d57e096157feb9f900cbb233c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 23 Jun 2007 20:17:58 -0400 Subject: SUNRPC: Mark auth and cred operation tables as constant. Also do the same for gss_api operation tables. Signed-off-by: Trond Myklebust --- net/sunrpc/auth.c | 8 ++++---- net/sunrpc/auth_gss/auth_gss.c | 8 ++++---- net/sunrpc/auth_gss/gss_krb5_mech.c | 2 +- net/sunrpc/auth_gss/gss_spkm3_mech.c | 2 +- net/sunrpc/auth_null.c | 4 ++-- net/sunrpc/auth_unix.c | 6 +++--- 6 files changed, 15 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 1686dc7..d3f0f94 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -19,7 +19,7 @@ #endif static DEFINE_SPINLOCK(rpc_authflavor_lock); -static struct rpc_authops * auth_flavors[RPC_AUTH_MAXFLAVOR] = { +static const struct rpc_authops *auth_flavors[RPC_AUTH_MAXFLAVOR] = { &authnull_ops, /* AUTH_NULL */ &authunix_ops, /* AUTH_UNIX */ NULL, /* others can be loadable modules */ @@ -33,7 +33,7 @@ pseudoflavor_to_flavor(u32 flavor) { } int -rpcauth_register(struct rpc_authops *ops) +rpcauth_register(const struct rpc_authops *ops) { rpc_authflavor_t flavor; int ret = -EPERM; @@ -50,7 +50,7 @@ rpcauth_register(struct rpc_authops *ops) } int -rpcauth_unregister(struct rpc_authops *ops) +rpcauth_unregister(const struct rpc_authops *ops) { rpc_authflavor_t flavor; int ret = -EPERM; @@ -70,7 +70,7 @@ struct rpc_auth * rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt) { struct rpc_auth *auth; - struct rpc_authops *ops; + const struct rpc_authops *ops; u32 flavor = pseudoflavor_to_flavor(pseudoflavor); auth = ERR_PTR(-EINVAL); diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 459dc9b..177a9e4 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -54,9 +54,9 @@ #include #include -static struct rpc_authops authgss_ops; +static const struct rpc_authops authgss_ops; -static struct rpc_credops gss_credops; +static const struct rpc_credops gss_credops; #ifdef RPC_DEBUG # define RPCDBG_FACILITY RPCDBG_AUTH @@ -1193,7 +1193,7 @@ out: return status; } -static struct rpc_authops authgss_ops = { +static const struct rpc_authops authgss_ops = { .owner = THIS_MODULE, .au_flavor = RPC_AUTH_GSS, #ifdef RPC_DEBUG @@ -1205,7 +1205,7 @@ static struct rpc_authops authgss_ops = { .crcreate = gss_create_cred }; -static struct rpc_credops gss_credops = { +static const struct rpc_credops gss_credops = { .cr_name = "AUTH_GSS", .crdestroy = gss_destroy_cred, .cr_init = gss_cred_init, diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index 7b19432..71b9dae 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c @@ -201,7 +201,7 @@ gss_delete_sec_context_kerberos(void *internal_ctx) { kfree(kctx); } -static struct gss_api_ops gss_kerberos_ops = { +static const struct gss_api_ops gss_kerberos_ops = { .gss_import_sec_context = gss_import_sec_context_kerberos, .gss_get_mic = gss_get_mic_kerberos, .gss_verify_mic = gss_verify_mic_kerberos, diff --git a/net/sunrpc/auth_gss/gss_spkm3_mech.c b/net/sunrpc/auth_gss/gss_spkm3_mech.c index 7e15aa6..577d590 100644 --- a/net/sunrpc/auth_gss/gss_spkm3_mech.c +++ b/net/sunrpc/auth_gss/gss_spkm3_mech.c @@ -202,7 +202,7 @@ gss_get_mic_spkm3(struct gss_ctx *ctx, return err; } -static struct gss_api_ops gss_spkm3_ops = { +static const struct gss_api_ops gss_spkm3_ops = { .gss_import_sec_context = gss_import_sec_context_spkm3, .gss_get_mic = gss_get_mic_spkm3, .gss_verify_mic = gss_verify_mic_spkm3, diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c index 890bd9b..fe9b6aa 100644 --- a/net/sunrpc/auth_null.c +++ b/net/sunrpc/auth_null.c @@ -101,7 +101,7 @@ nul_validate(struct rpc_task *task, __be32 *p) return p; } -struct rpc_authops authnull_ops = { +const struct rpc_authops authnull_ops = { .owner = THIS_MODULE, .au_flavor = RPC_AUTH_NULL, #ifdef RPC_DEBUG @@ -122,7 +122,7 @@ struct rpc_auth null_auth = { }; static -struct rpc_credops null_credops = { +const struct rpc_credops null_credops = { .cr_name = "AUTH_NULL", .crdestroy = nul_destroy_cred, .crmatch = nul_match, diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index e54782e..6600c7a 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -34,7 +34,7 @@ struct unx_cred { static struct rpc_auth unix_auth; static struct rpc_cred_cache unix_cred_cache; -static struct rpc_credops unix_credops; +static const struct rpc_credops unix_credops; static struct rpc_auth * unx_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) @@ -205,7 +205,7 @@ unx_validate(struct rpc_task *task, __be32 *p) return p; } -struct rpc_authops authunix_ops = { +const struct rpc_authops authunix_ops = { .owner = THIS_MODULE, .au_flavor = RPC_AUTH_UNIX, #ifdef RPC_DEBUG @@ -233,7 +233,7 @@ struct rpc_auth unix_auth = { }; static -struct rpc_credops unix_credops = { +const struct rpc_credops unix_credops = { .cr_name = "AUTH_UNIX", .crdestroy = unx_destroy_cred, .crmatch = unx_match, -- cgit v1.1 From 5fe4755e2526a2aa82b7ed8daeb3aed74a236925 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 23 Jun 2007 19:55:31 -0400 Subject: SUNRPC: Clean up rpc credential initialisation Add a helper rpc_cred_init() Signed-off-by: Trond Myklebust --- net/sunrpc/auth.c | 24 ++++++++++++++++++------ net/sunrpc/auth_gss/auth_gss.c | 6 +----- net/sunrpc/auth_unix.c | 10 ++-------- 3 files changed, 21 insertions(+), 19 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index d3f0f94..2156327 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -264,13 +264,9 @@ retry: if (!cred) { new = auth->au_ops->crcreate(auth, acred, flags); - if (!IS_ERR(new)) { -#ifdef RPC_DEBUG - new->cr_magic = RPCAUTH_CRED_MAGIC; -#endif + if (!IS_ERR(new)) goto retry; - } else - cred = new; + cred = new; } else if ((cred->cr_flags & RPCAUTH_CRED_NEW) && cred->cr_ops->cr_init != NULL && !(flags & RPCAUTH_LOOKUP_NEW)) { @@ -302,6 +298,22 @@ rpcauth_lookupcred(struct rpc_auth *auth, int flags) return ret; } +void +rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred, + struct rpc_auth *auth, const struct rpc_credops *ops) +{ + INIT_HLIST_NODE(&cred->cr_hash); + atomic_set(&cred->cr_count, 1); + cred->cr_auth = auth; + cred->cr_ops = ops; + cred->cr_expire = jiffies; +#ifdef RPC_DEBUG + cred->cr_magic = RPCAUTH_CRED_MAGIC; +#endif + cred->cr_uid = acred->uid; +} +EXPORT_SYMBOL(rpcauth_init_cred); + struct rpc_cred * rpcauth_bindcred(struct rpc_task *task) { diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 177a9e4..766de0a 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -727,15 +727,11 @@ gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) if (!(cred = kzalloc(sizeof(*cred), GFP_KERNEL))) goto out_err; - atomic_set(&cred->gc_count, 1); - cred->gc_uid = acred->uid; + rpcauth_init_cred(&cred->gc_base, acred, auth, &gss_credops); /* * Note: in order to force a call to call_refresh(), we deliberately * fail to flag the credential as RPCAUTH_CRED_UPTODATE. */ - cred->gc_flags = 0; - cred->gc_base.cr_auth = auth; - cred->gc_base.cr_ops = &gss_credops; cred->gc_base.cr_flags = RPCAUTH_CRED_NEW; cred->gc_service = gss_auth->service; return &cred->gc_base; diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 6600c7a..2f1bdb5 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -20,9 +20,6 @@ struct unx_cred { gid_t uc_gids[NFS_NGROUPS]; }; #define uc_uid uc_base.cr_uid -#define uc_count uc_base.cr_count -#define uc_flags uc_base.cr_flags -#define uc_expire uc_base.cr_expire #define UNX_CRED_EXPIRE (60 * HZ) @@ -74,8 +71,8 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) if (!(cred = kmalloc(sizeof(*cred), GFP_KERNEL))) return ERR_PTR(-ENOMEM); - atomic_set(&cred->uc_count, 1); - cred->uc_flags = RPCAUTH_CRED_UPTODATE; + rpcauth_init_cred(&cred->uc_base, acred, auth, &unix_credops); + cred->uc_base.cr_flags = RPCAUTH_CRED_UPTODATE; if (flags & RPCAUTH_LOOKUP_ROOTCREDS) { cred->uc_uid = 0; cred->uc_gid = 0; @@ -85,15 +82,12 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) if (groups > NFS_NGROUPS) groups = NFS_NGROUPS; - cred->uc_uid = acred->uid; cred->uc_gid = acred->gid; for (i = 0; i < groups; i++) cred->uc_gids[i] = GROUP_AT(acred->group_info, i); if (i < NFS_NGROUPS) cred->uc_gids[i] = NOGROUP; } - cred->uc_base.cr_auth = &unix_auth; - cred->uc_base.cr_ops = &unix_credops; return (struct rpc_cred *) cred; } -- cgit v1.1 From 696e38df9d1b256e97b077ecde7afb8dd60364fd Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 25 Jun 2007 09:48:25 -0400 Subject: SUNRPC: replace casts in auth_unix.c with container_of() Signed-off-by: Trond Myklebust --- net/sunrpc/auth_unix.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 2f1bdb5..f17dabb 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -89,12 +89,14 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) cred->uc_gids[i] = NOGROUP; } - return (struct rpc_cred *) cred; + return &cred->uc_base; } static void -unx_destroy_cred(struct rpc_cred *cred) +unx_destroy_cred(struct rpc_cred *rcred) { + struct unx_cred *cred = container_of(rcred, struct unx_cred, uc_base); + kfree(cred); } @@ -106,7 +108,7 @@ unx_destroy_cred(struct rpc_cred *cred) static int unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags) { - struct unx_cred *cred = (struct unx_cred *) rcred; + struct unx_cred *cred = container_of(rcred, struct unx_cred, uc_base); int i; if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS)) { @@ -137,7 +139,7 @@ static __be32 * unx_marshal(struct rpc_task *task, __be32 *p) { struct rpc_clnt *clnt = task->tk_client; - struct unx_cred *cred = (struct unx_cred *) task->tk_msg.rpc_cred; + struct unx_cred *cred = container_of(task->tk_msg.rpc_cred, struct unx_cred, uc_base); __be32 *base, *hold; int i; -- cgit v1.1 From fc432dd90760a629c57026e57f65ff80a1a31d2f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 25 Jun 2007 10:15:15 -0400 Subject: SUNRPC: Enforce atomic updates of rpc_cred->cr_flags Convert to the use of atomic bitops... Signed-off-by: Trond Myklebust --- net/sunrpc/auth.c | 22 ++++++++++++---------- net/sunrpc/auth_gss/auth_gss.c | 22 +++++++++++----------- net/sunrpc/auth_null.c | 4 ++-- net/sunrpc/auth_unix.c | 4 ++-- 4 files changed, 27 insertions(+), 25 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 2156327..4d7c78b 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -190,8 +190,8 @@ rpcauth_prune_expired(struct rpc_auth *auth, struct rpc_cred *cred, struct hlist if (atomic_read(&cred->cr_count) != 1) return; if (time_after(jiffies, cred->cr_expire + auth->au_credcache->expire)) - cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; - if (!(cred->cr_flags & RPCAUTH_CRED_UPTODATE)) { + clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); + if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0) { __hlist_del(&cred->cr_hash); hlist_add_head(&cred->cr_hash, free); } @@ -267,7 +267,7 @@ retry: if (!IS_ERR(new)) goto retry; cred = new; - } else if ((cred->cr_flags & RPCAUTH_CRED_NEW) + } else if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) && cred->cr_ops->cr_init != NULL && !(flags & RPCAUTH_LOOKUP_NEW)) { int res = cred->cr_ops->cr_init(auth, cred); @@ -440,17 +440,19 @@ rpcauth_refreshcred(struct rpc_task *task) void rpcauth_invalcred(struct rpc_task *task) { + struct rpc_cred *cred = task->tk_msg.rpc_cred; + dprintk("RPC: %5u invalidating %s cred %p\n", - task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred); - spin_lock(&rpc_credcache_lock); - if (task->tk_msg.rpc_cred) - task->tk_msg.rpc_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; - spin_unlock(&rpc_credcache_lock); + task->tk_pid, task->tk_auth->au_ops->au_name, cred); + if (cred) + clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); } int rpcauth_uptodatecred(struct rpc_task *task) { - return !(task->tk_msg.rpc_cred) || - (task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_UPTODATE); + struct rpc_cred *cred = task->tk_msg.rpc_cred; + + return cred == NULL || + test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0; } diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 766de0a..55c47ae 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -114,8 +114,8 @@ gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx) write_lock(&gss_ctx_lock); old = gss_cred->gc_ctx; gss_cred->gc_ctx = ctx; - cred->cr_flags |= RPCAUTH_CRED_UPTODATE; - cred->cr_flags &= ~RPCAUTH_CRED_NEW; + set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); + clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags); write_unlock(&gss_ctx_lock); if (old) gss_put_ctx(old); @@ -128,7 +128,7 @@ gss_cred_is_uptodate_ctx(struct rpc_cred *cred) int res = 0; read_lock(&gss_ctx_lock); - if ((cred->cr_flags & RPCAUTH_CRED_UPTODATE) && gss_cred->gc_ctx) + if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) && gss_cred->gc_ctx) res = 1; read_unlock(&gss_ctx_lock); return res; @@ -732,7 +732,7 @@ gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) * Note: in order to force a call to call_refresh(), we deliberately * fail to flag the credential as RPCAUTH_CRED_UPTODATE. */ - cred->gc_base.cr_flags = RPCAUTH_CRED_NEW; + cred->gc_base.cr_flags = 1UL << RPCAUTH_CRED_NEW; cred->gc_service = gss_auth->service; return &cred->gc_base; @@ -764,7 +764,7 @@ gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags) * we don't really care if the credential has expired or not, * since the caller should be prepared to reinitialise it. */ - if ((flags & RPCAUTH_LOOKUP_NEW) && (rc->cr_flags & RPCAUTH_CRED_NEW)) + if ((flags & RPCAUTH_LOOKUP_NEW) && test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags)) goto out; /* Don't match with creds that have expired. */ if (gss_cred->gc_ctx && time_after(jiffies, gss_cred->gc_ctx->gc_expiry)) @@ -820,7 +820,7 @@ gss_marshal(struct rpc_task *task, __be32 *p) mic.data = (u8 *)(p + 1); maj_stat = gss_get_mic(ctx->gc_gss_ctx, &verf_buf, &mic); if (maj_stat == GSS_S_CONTEXT_EXPIRED) { - cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; + clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); } else if (maj_stat != 0) { printk("gss_marshal: gss_get_mic FAILED (%d)\n", maj_stat); goto out_put_ctx; @@ -873,7 +873,7 @@ gss_validate(struct rpc_task *task, __be32 *p) maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic); if (maj_stat == GSS_S_CONTEXT_EXPIRED) - cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; + clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); if (maj_stat) goto out_bad; /* We leave it to unwrap to calculate au_rslack. For now we just @@ -927,7 +927,7 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, maj_stat = gss_get_mic(ctx->gc_gss_ctx, &integ_buf, &mic); status = -EIO; /* XXX? */ if (maj_stat == GSS_S_CONTEXT_EXPIRED) - cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; + clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); else if (maj_stat) return status; q = xdr_encode_opaque(p, NULL, mic.len); @@ -1026,7 +1026,7 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, /* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was * done anyway, so it's safe to put the request on the wire: */ if (maj_stat == GSS_S_CONTEXT_EXPIRED) - cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; + clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); else if (maj_stat) return status; @@ -1113,7 +1113,7 @@ gss_unwrap_resp_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &integ_buf, &mic); if (maj_stat == GSS_S_CONTEXT_EXPIRED) - cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; + clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); if (maj_stat != GSS_S_COMPLETE) return status; return 0; @@ -1138,7 +1138,7 @@ gss_unwrap_resp_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset, rcv_buf); if (maj_stat == GSS_S_CONTEXT_EXPIRED) - cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; + clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); if (maj_stat != GSS_S_COMPLETE) return status; if (ntohl(*(*p)++) != rqstp->rq_seqno) diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c index fe9b6aa..6c905fb 100644 --- a/net/sunrpc/auth_null.c +++ b/net/sunrpc/auth_null.c @@ -76,7 +76,7 @@ nul_marshal(struct rpc_task *task, __be32 *p) static int nul_refresh(struct rpc_task *task) { - task->tk_msg.rpc_cred->cr_flags |= RPCAUTH_CRED_UPTODATE; + set_bit(RPCAUTH_CRED_UPTODATE, &task->tk_msg.rpc_cred->cr_flags); return 0; } @@ -136,7 +136,7 @@ struct rpc_cred null_cred = { .cr_auth = &null_auth, .cr_ops = &null_credops, .cr_count = ATOMIC_INIT(1), - .cr_flags = RPCAUTH_CRED_UPTODATE, + .cr_flags = 1UL << RPCAUTH_CRED_UPTODATE, #ifdef RPC_DEBUG .cr_magic = RPCAUTH_CRED_MAGIC, #endif diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index f17dabb..29d50ff 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -72,7 +72,7 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) return ERR_PTR(-ENOMEM); rpcauth_init_cred(&cred->uc_base, acred, auth, &unix_credops); - cred->uc_base.cr_flags = RPCAUTH_CRED_UPTODATE; + cred->uc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE; if (flags & RPCAUTH_LOOKUP_ROOTCREDS) { cred->uc_uid = 0; cred->uc_gid = 0; @@ -172,7 +172,7 @@ unx_marshal(struct rpc_task *task, __be32 *p) static int unx_refresh(struct rpc_task *task) { - task->tk_msg.rpc_cred->cr_flags |= RPCAUTH_CRED_UPTODATE; + set_bit(RPCAUTH_CRED_UPTODATE, &task->tk_msg.rpc_cred->cr_flags); return 0; } -- cgit v1.1 From e092bdcd939416ef911090890096fe07d0281a5e Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 23 Jun 2007 19:45:36 -0400 Subject: SUNRPC: cleanup rpc credential cache garbage collection Signed-off-by: Trond Myklebust --- net/sunrpc/auth.c | 121 +++++++++++++++++++++++++++++-------------------- net/sunrpc/auth_null.c | 1 + 2 files changed, 73 insertions(+), 49 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 4d7c78b..00f9649 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -25,6 +25,8 @@ static const struct rpc_authops *auth_flavors[RPC_AUTH_MAXFLAVOR] = { NULL, /* others can be loadable modules */ }; +static LIST_HEAD(cred_unused); + static u32 pseudoflavor_to_flavor(u32 flavor) { if (flavor >= RPC_AUTH_MAXFLAVOR) @@ -134,13 +136,13 @@ rpcauth_init_credcache(struct rpc_auth *auth, unsigned long expire) * Destroy a list of credentials */ static inline -void rpcauth_destroy_credlist(struct hlist_head *head) +void rpcauth_destroy_credlist(struct list_head *head) { struct rpc_cred *cred; - while (!hlist_empty(head)) { - cred = hlist_entry(head->first, struct rpc_cred, cr_hash); - hlist_del_init(&cred->cr_hash); + while (!list_empty(head)) { + cred = list_entry(head->next, struct rpc_cred, cr_lru); + list_del_init(&cred->cr_lru); put_rpccred(cred); } } @@ -152,17 +154,20 @@ void rpcauth_destroy_credlist(struct hlist_head *head) void rpcauth_clear_credcache(struct rpc_cred_cache *cache) { - HLIST_HEAD(free); - struct hlist_node *pos, *next; + LIST_HEAD(free); + struct hlist_head *head; struct rpc_cred *cred; int i; spin_lock(&rpc_credcache_lock); for (i = 0; i < RPC_CREDCACHE_NR; i++) { - hlist_for_each_safe(pos, next, &cache->hashtable[i]) { - cred = hlist_entry(pos, struct rpc_cred, cr_hash); - __hlist_del(&cred->cr_hash); - hlist_add_head(&cred->cr_hash, &free); + head = &cache->hashtable[i]; + while (!hlist_empty(head)) { + cred = hlist_entry(head->first, struct rpc_cred, cr_hash); + get_rpccred(cred); + list_move_tail(&cred->cr_lru, &free); + smp_wmb(); + hlist_del_init(&cred->cr_hash); } } spin_unlock(&rpc_credcache_lock); @@ -184,38 +189,39 @@ rpcauth_destroy_credcache(struct rpc_auth *auth) } } +/* + * Remove stale credentials. Avoid sleeping inside the loop. + */ static void -rpcauth_prune_expired(struct rpc_auth *auth, struct rpc_cred *cred, struct hlist_head *free) +rpcauth_prune_expired(struct list_head *free) { - if (atomic_read(&cred->cr_count) != 1) - return; - if (time_after(jiffies, cred->cr_expire + auth->au_credcache->expire)) - clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); - if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0) { - __hlist_del(&cred->cr_hash); - hlist_add_head(&cred->cr_hash, free); + struct rpc_cred *cred; + + while (!list_empty(&cred_unused)) { + cred = list_entry(cred_unused.next, struct rpc_cred, cr_lru); + if (time_after(jiffies, cred->cr_expire + + cred->cr_auth->au_credcache->expire)) + break; + list_del_init(&cred->cr_lru); + if (atomic_read(&cred->cr_count) != 0) + continue; + get_rpccred(cred); + list_add_tail(&cred->cr_lru, free); + smp_wmb(); + hlist_del_init(&cred->cr_hash); } } /* - * Remove stale credentials. Avoid sleeping inside the loop. + * Run garbage collector. */ static void -rpcauth_gc_credcache(struct rpc_auth *auth, struct hlist_head *free) +rpcauth_gc_credcache(struct rpc_cred_cache *cache, struct list_head *free) { - struct rpc_cred_cache *cache = auth->au_credcache; - struct hlist_node *pos, *next; - struct rpc_cred *cred; - int i; - - dprintk("RPC: gc'ing RPC credentials for auth %p\n", auth); - for (i = 0; i < RPC_CREDCACHE_NR; i++) { - hlist_for_each_safe(pos, next, &cache->hashtable[i]) { - cred = hlist_entry(pos, struct rpc_cred, cr_hash); - rpcauth_prune_expired(auth, cred, free); - } - } + if (time_before(jiffies, cache->nextgc)) + return; cache->nextgc = jiffies + cache->expire; + rpcauth_prune_expired(free); } /* @@ -225,39 +231,35 @@ struct rpc_cred * rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, int flags) { + LIST_HEAD(free); struct rpc_cred_cache *cache = auth->au_credcache; - HLIST_HEAD(free); - struct hlist_node *pos, *next; + struct hlist_node *pos; struct rpc_cred *new = NULL, - *cred = NULL; + *cred = NULL, + *entry; int nr = 0; if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS)) nr = acred->uid & RPC_CREDCACHE_MASK; retry: spin_lock(&rpc_credcache_lock); - if (time_before(cache->nextgc, jiffies)) - rpcauth_gc_credcache(auth, &free); - hlist_for_each_safe(pos, next, &cache->hashtable[nr]) { - struct rpc_cred *entry; - entry = hlist_entry(pos, struct rpc_cred, cr_hash); - if (entry->cr_ops->crmatch(acred, entry, flags)) { - hlist_del(&entry->cr_hash); - cred = entry; - break; - } - rpcauth_prune_expired(auth, entry, &free); + hlist_for_each_entry(entry, pos, &cache->hashtable[nr], cr_hash) { + if (!entry->cr_ops->crmatch(acred, entry, flags)) + continue; + cred = get_rpccred(entry); + hlist_del(&entry->cr_hash); + break; } if (new) { if (cred) - hlist_add_head(&new->cr_hash, &free); + list_add_tail(&new->cr_lru, &free); else cred = new; } if (cred) { hlist_add_head(&cred->cr_hash, &cache->hashtable[nr]); - get_rpccred(cred); } + rpcauth_gc_credcache(cache, &free); spin_unlock(&rpc_credcache_lock); rpcauth_destroy_credlist(&free); @@ -303,6 +305,7 @@ rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred, struct rpc_auth *auth, const struct rpc_credops *ops) { INIT_HLIST_NODE(&cred->cr_hash); + INIT_LIST_HEAD(&cred->cr_lru); atomic_set(&cred->cr_count, 1); cred->cr_auth = auth; cred->cr_ops = ops; @@ -353,9 +356,29 @@ rpcauth_holdcred(struct rpc_task *task) void put_rpccred(struct rpc_cred *cred) { - cred->cr_expire = jiffies; + /* Fast path for unhashed credentials */ + if (!hlist_unhashed(&cred->cr_hash)) + goto need_lock; + if (!atomic_dec_and_test(&cred->cr_count)) return; + goto out_destroy; + +need_lock: + if (!atomic_dec_and_lock(&cred->cr_count, &rpc_credcache_lock)) + return; + if (!list_empty(&cred->cr_lru)) + list_del_init(&cred->cr_lru); + if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0) + hlist_del(&cred->cr_hash); + else if (!hlist_unhashed(&cred->cr_hash)) { + cred->cr_expire = jiffies; + list_add_tail(&cred->cr_lru, &cred_unused); + spin_unlock(&rpc_credcache_lock); + return; + } + spin_unlock(&rpc_credcache_lock); +out_destroy: cred->cr_ops->crdestroy(cred); } diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c index 6c905fb..537d0e8 100644 --- a/net/sunrpc/auth_null.c +++ b/net/sunrpc/auth_null.c @@ -133,6 +133,7 @@ const struct rpc_credops null_credops = { static struct rpc_cred null_cred = { + .cr_lru = LIST_HEAD_INIT(null_cred.cr_lru), .cr_auth = &null_auth, .cr_ops = &null_credops, .cr_count = ATOMIC_INIT(1), -- cgit v1.1 From 31be5bf15f3dafffce110eb1afadccbf2e3067b4 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sun, 24 Jun 2007 15:55:26 -0400 Subject: SUNRPC: Convert the credcache lookup code to use RCU Signed-off-by: Trond Myklebust --- net/sunrpc/auth.c | 91 ++++++++++++++++++++++++++---------------- net/sunrpc/auth_gss/auth_gss.c | 22 +++++++--- net/sunrpc/auth_unix.c | 18 +++++++-- 3 files changed, 88 insertions(+), 43 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 00f9649..ad7bde2 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -112,6 +112,14 @@ rpcauth_release(struct rpc_auth *auth) static DEFINE_SPINLOCK(rpc_credcache_lock); +static void +rpcauth_unhash_cred_locked(struct rpc_cred *cred) +{ + hlist_del_rcu(&cred->cr_hash); + smp_mb__before_clear_bit(); + clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags); +} + /* * Initialize RPC credential cache */ @@ -166,8 +174,7 @@ rpcauth_clear_credcache(struct rpc_cred_cache *cache) cred = hlist_entry(head->first, struct rpc_cred, cr_hash); get_rpccred(cred); list_move_tail(&cred->cr_lru, &free); - smp_wmb(); - hlist_del_init(&cred->cr_hash); + rpcauth_unhash_cred_locked(cred); } } spin_unlock(&rpc_credcache_lock); @@ -207,8 +214,7 @@ rpcauth_prune_expired(struct list_head *free) continue; get_rpccred(cred); list_add_tail(&cred->cr_lru, free); - smp_wmb(); - hlist_del_init(&cred->cr_hash); + rpcauth_unhash_cred_locked(cred); } } @@ -218,10 +224,12 @@ rpcauth_prune_expired(struct list_head *free) static void rpcauth_gc_credcache(struct rpc_cred_cache *cache, struct list_head *free) { - if (time_before(jiffies, cache->nextgc)) + if (list_empty(&cred_unused) || time_before(jiffies, cache->nextgc)) return; + spin_lock(&rpc_credcache_lock); cache->nextgc = jiffies + cache->expire; rpcauth_prune_expired(free); + spin_unlock(&rpc_credcache_lock); } /* @@ -234,42 +242,57 @@ rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, LIST_HEAD(free); struct rpc_cred_cache *cache = auth->au_credcache; struct hlist_node *pos; - struct rpc_cred *new = NULL, - *cred = NULL, - *entry; + struct rpc_cred *cred = NULL, + *entry, *new; int nr = 0; if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS)) nr = acred->uid & RPC_CREDCACHE_MASK; -retry: - spin_lock(&rpc_credcache_lock); - hlist_for_each_entry(entry, pos, &cache->hashtable[nr], cr_hash) { + + rcu_read_lock(); + hlist_for_each_entry_rcu(entry, pos, &cache->hashtable[nr], cr_hash) { if (!entry->cr_ops->crmatch(acred, entry, flags)) continue; + spin_lock(&rpc_credcache_lock); + if (test_bit(RPCAUTH_CRED_HASHED, &entry->cr_flags) == 0) { + spin_unlock(&rpc_credcache_lock); + continue; + } cred = get_rpccred(entry); - hlist_del(&entry->cr_hash); + spin_unlock(&rpc_credcache_lock); break; } - if (new) { - if (cred) - list_add_tail(&new->cr_lru, &free); - else - cred = new; - } - if (cred) { - hlist_add_head(&cred->cr_hash, &cache->hashtable[nr]); + rcu_read_unlock(); + + if (cred != NULL) { + rpcauth_gc_credcache(cache, &free); + goto found; } - rpcauth_gc_credcache(cache, &free); - spin_unlock(&rpc_credcache_lock); - rpcauth_destroy_credlist(&free); + new = auth->au_ops->crcreate(auth, acred, flags); + if (IS_ERR(new)) { + cred = new; + goto out; + } - if (!cred) { - new = auth->au_ops->crcreate(auth, acred, flags); - if (!IS_ERR(new)) - goto retry; + spin_lock(&rpc_credcache_lock); + hlist_for_each_entry(entry, pos, &cache->hashtable[nr], cr_hash) { + if (!entry->cr_ops->crmatch(acred, entry, flags)) + continue; + cred = get_rpccred(entry); + break; + } + if (cred == NULL) { cred = new; - } else if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) + set_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags); + hlist_add_head_rcu(&cred->cr_hash, &cache->hashtable[nr]); + } else + list_add_tail(&new->cr_lru, &free); + rpcauth_prune_expired(&free); + cache->nextgc = jiffies + cache->expire; + spin_unlock(&rpc_credcache_lock); +found: + if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) && cred->cr_ops->cr_init != NULL && !(flags & RPCAUTH_LOOKUP_NEW)) { int res = cred->cr_ops->cr_init(auth, cred); @@ -278,8 +301,9 @@ retry: cred = ERR_PTR(res); } } - - return (struct rpc_cred *) cred; + rpcauth_destroy_credlist(&free); +out: + return cred; } struct rpc_cred * @@ -357,21 +381,20 @@ void put_rpccred(struct rpc_cred *cred) { /* Fast path for unhashed credentials */ - if (!hlist_unhashed(&cred->cr_hash)) + if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0) goto need_lock; if (!atomic_dec_and_test(&cred->cr_count)) return; goto out_destroy; - need_lock: if (!atomic_dec_and_lock(&cred->cr_count, &rpc_credcache_lock)) return; if (!list_empty(&cred->cr_lru)) list_del_init(&cred->cr_lru); if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0) - hlist_del(&cred->cr_hash); - else if (!hlist_unhashed(&cred->cr_hash)) { + rpcauth_unhash_cred_locked(cred); + else if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0) { cred->cr_expire = jiffies; list_add_tail(&cred->cr_lru, &cred_unused); spin_unlock(&rpc_credcache_lock); diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 55c47ae..068fa6d 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -694,15 +694,25 @@ gss_destroy_ctx(struct gss_cl_ctx *ctx) } static void -gss_destroy_cred(struct rpc_cred *rc) +gss_free_cred(struct gss_cred *gss_cred) { - struct gss_cred *cred = container_of(rc, struct gss_cred, gc_base); + dprintk("RPC: gss_free_cred %p\n", gss_cred); + if (gss_cred->gc_ctx) + gss_put_ctx(gss_cred->gc_ctx); + kfree(gss_cred); +} - dprintk("RPC: gss_destroy_cred \n"); +static void +gss_free_cred_callback(struct rcu_head *head) +{ + struct gss_cred *gss_cred = container_of(head, struct gss_cred, gc_base.cr_rcu); + gss_free_cred(gss_cred); +} - if (cred->gc_ctx) - gss_put_ctx(cred->gc_ctx); - kfree(cred); +static void +gss_destroy_cred(struct rpc_cred *cred) +{ + call_rcu(&cred->cr_rcu, gss_free_cred_callback); } /* diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 29d50ff..f7ff6ad 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -93,11 +93,23 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) } static void -unx_destroy_cred(struct rpc_cred *rcred) +unx_free_cred(struct unx_cred *unx_cred) { - struct unx_cred *cred = container_of(rcred, struct unx_cred, uc_base); + dprintk("RPC: unx_free_cred %p\n", unx_cred); + kfree(unx_cred); +} + +static void +unx_free_cred_callback(struct rcu_head *head) +{ + struct unx_cred *unx_cred = container_of(head, struct unx_cred, uc_base.cr_rcu); + unx_free_cred(unx_cred); +} - kfree(cred); +static void +unx_destroy_cred(struct rpc_cred *cred) +{ + call_rcu(&cred->cr_rcu, unx_free_cred_callback); } /* -- cgit v1.1 From 9499b4341b56935f61af9e7e354e7d11e70f5258 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sun, 24 Jun 2007 15:57:57 -0400 Subject: SUNRPC: Give credential cache a local spinlock Signed-off-by: Trond Myklebust --- net/sunrpc/auth.c | 46 ++++++++++++++++++++++++++++++++-------------- net/sunrpc/auth_unix.c | 5 +++++ net/sunrpc/sunrpc_syms.c | 1 + 3 files changed, 38 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index ad7bde2..cf1198d 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -120,6 +120,18 @@ rpcauth_unhash_cred_locked(struct rpc_cred *cred) clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags); } +static void +rpcauth_unhash_cred(struct rpc_cred *cred) +{ + spinlock_t *cache_lock; + + cache_lock = &cred->cr_auth->au_credcache->lock; + spin_lock(cache_lock); + if (atomic_read(&cred->cr_count) == 0) + rpcauth_unhash_cred_locked(cred); + spin_unlock(cache_lock); +} + /* * Initialize RPC credential cache */ @@ -134,6 +146,7 @@ rpcauth_init_credcache(struct rpc_auth *auth, unsigned long expire) return -ENOMEM; for (i = 0; i < RPC_CREDCACHE_NR; i++) INIT_HLIST_HEAD(&new->hashtable[i]); + spin_lock_init(&new->lock); new->expire = expire; new->nextgc = jiffies + (expire >> 1); auth->au_credcache = new; @@ -168,6 +181,7 @@ rpcauth_clear_credcache(struct rpc_cred_cache *cache) int i; spin_lock(&rpc_credcache_lock); + spin_lock(&cache->lock); for (i = 0; i < RPC_CREDCACHE_NR; i++) { head = &cache->hashtable[i]; while (!hlist_empty(head)) { @@ -177,6 +191,7 @@ rpcauth_clear_credcache(struct rpc_cred_cache *cache) rpcauth_unhash_cred_locked(cred); } } + spin_unlock(&cache->lock); spin_unlock(&rpc_credcache_lock); rpcauth_destroy_credlist(&free); } @@ -202,6 +217,7 @@ rpcauth_destroy_credcache(struct rpc_auth *auth) static void rpcauth_prune_expired(struct list_head *free) { + spinlock_t *cache_lock; struct rpc_cred *cred; while (!list_empty(&cred_unused)) { @@ -212,9 +228,14 @@ rpcauth_prune_expired(struct list_head *free) list_del_init(&cred->cr_lru); if (atomic_read(&cred->cr_count) != 0) continue; - get_rpccred(cred); - list_add_tail(&cred->cr_lru, free); - rpcauth_unhash_cred_locked(cred); + cache_lock = &cred->cr_auth->au_credcache->lock; + spin_lock(cache_lock); + if (atomic_read(&cred->cr_count) == 0) { + get_rpccred(cred); + list_add_tail(&cred->cr_lru, free); + rpcauth_unhash_cred_locked(cred); + } + spin_unlock(cache_lock); } } @@ -253,21 +274,19 @@ rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, hlist_for_each_entry_rcu(entry, pos, &cache->hashtable[nr], cr_hash) { if (!entry->cr_ops->crmatch(acred, entry, flags)) continue; - spin_lock(&rpc_credcache_lock); + spin_lock(&cache->lock); if (test_bit(RPCAUTH_CRED_HASHED, &entry->cr_flags) == 0) { - spin_unlock(&rpc_credcache_lock); + spin_unlock(&cache->lock); continue; } cred = get_rpccred(entry); - spin_unlock(&rpc_credcache_lock); + spin_unlock(&cache->lock); break; } rcu_read_unlock(); - if (cred != NULL) { - rpcauth_gc_credcache(cache, &free); + if (cred != NULL) goto found; - } new = auth->au_ops->crcreate(auth, acred, flags); if (IS_ERR(new)) { @@ -275,7 +294,7 @@ rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, goto out; } - spin_lock(&rpc_credcache_lock); + spin_lock(&cache->lock); hlist_for_each_entry(entry, pos, &cache->hashtable[nr], cr_hash) { if (!entry->cr_ops->crmatch(acred, entry, flags)) continue; @@ -288,9 +307,7 @@ rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, hlist_add_head_rcu(&cred->cr_hash, &cache->hashtable[nr]); } else list_add_tail(&new->cr_lru, &free); - rpcauth_prune_expired(&free); - cache->nextgc = jiffies + cache->expire; - spin_unlock(&rpc_credcache_lock); + spin_unlock(&cache->lock); found: if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) && cred->cr_ops->cr_init != NULL @@ -301,6 +318,7 @@ found: cred = ERR_PTR(res); } } + rpcauth_gc_credcache(cache, &free); rpcauth_destroy_credlist(&free); out: return cred; @@ -393,7 +411,7 @@ need_lock: if (!list_empty(&cred->cr_lru)) list_del_init(&cred->cr_lru); if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0) - rpcauth_unhash_cred_locked(cred); + rpcauth_unhash_cred(cred); else if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0) { cred->cr_expire = jiffies; list_add_tail(&cred->cr_lru, &cred_unused); diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index f7ff6ad..205878a 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -213,6 +213,11 @@ unx_validate(struct rpc_task *task, __be32 *p) return p; } +void __init rpc_init_authunix(void) +{ + spin_lock_init(&unix_cred_cache.lock); +} + const struct rpc_authops authunix_ops = { .owner = THIS_MODULE, .au_flavor = RPC_AUTH_UNIX, diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 3e19e7a..018065f 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -152,6 +152,7 @@ init_sunrpc(void) cache_register(&ip_map_cache); cache_register(&unix_gid_cache); init_socket_xprt(); + rpc_init_authunix(); out: return err; } -- cgit v1.1 From f5c2187cfef628784d8a09b6d0f77888246d0c0f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 25 Jun 2007 17:11:20 -0400 Subject: SUNRPC: Convert the credential garbage collector into a shrinker callback Signed-off-by: Trond Myklebust --- net/sunrpc/auth.c | 63 ++++++++++++++++++++++++++++++------------ net/sunrpc/auth_gss/auth_gss.c | 3 +- net/sunrpc/auth_unix.c | 6 +--- net/sunrpc/sunrpc_syms.c | 3 +- 4 files changed, 49 insertions(+), 26 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index cf1198d..81f4c77 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -26,6 +26,7 @@ static const struct rpc_authops *auth_flavors[RPC_AUTH_MAXFLAVOR] = { }; static LIST_HEAD(cred_unused); +static unsigned long number_cred_unused; static u32 pseudoflavor_to_flavor(u32 flavor) { @@ -136,7 +137,7 @@ rpcauth_unhash_cred(struct rpc_cred *cred) * Initialize RPC credential cache */ int -rpcauth_init_credcache(struct rpc_auth *auth, unsigned long expire) +rpcauth_init_credcache(struct rpc_auth *auth) { struct rpc_cred_cache *new; int i; @@ -147,8 +148,6 @@ rpcauth_init_credcache(struct rpc_auth *auth, unsigned long expire) for (i = 0; i < RPC_CREDCACHE_NR; i++) INIT_HLIST_HEAD(&new->hashtable[i]); spin_lock_init(&new->lock); - new->expire = expire; - new->nextgc = jiffies + (expire >> 1); auth->au_credcache = new; return 0; } @@ -187,7 +186,11 @@ rpcauth_clear_credcache(struct rpc_cred_cache *cache) while (!hlist_empty(head)) { cred = hlist_entry(head->first, struct rpc_cred, cr_hash); get_rpccred(cred); - list_move_tail(&cred->cr_lru, &free); + if (!list_empty(&cred->cr_lru)) { + list_del(&cred->cr_lru); + number_cred_unused--; + } + list_add_tail(&cred->cr_lru, &free); rpcauth_unhash_cred_locked(cred); } } @@ -214,18 +217,16 @@ rpcauth_destroy_credcache(struct rpc_auth *auth) /* * Remove stale credentials. Avoid sleeping inside the loop. */ -static void -rpcauth_prune_expired(struct list_head *free) +static int +rpcauth_prune_expired(struct list_head *free, int nr_to_scan) { spinlock_t *cache_lock; struct rpc_cred *cred; while (!list_empty(&cred_unused)) { cred = list_entry(cred_unused.next, struct rpc_cred, cr_lru); - if (time_after(jiffies, cred->cr_expire + - cred->cr_auth->au_credcache->expire)) - break; list_del_init(&cred->cr_lru); + number_cred_unused--; if (atomic_read(&cred->cr_count) != 0) continue; cache_lock = &cred->cr_auth->au_credcache->lock; @@ -234,23 +235,32 @@ rpcauth_prune_expired(struct list_head *free) get_rpccred(cred); list_add_tail(&cred->cr_lru, free); rpcauth_unhash_cred_locked(cred); + nr_to_scan--; } spin_unlock(cache_lock); + if (nr_to_scan == 0) + break; } + return nr_to_scan; } /* - * Run garbage collector. + * Run memory cache shrinker. */ -static void -rpcauth_gc_credcache(struct rpc_cred_cache *cache, struct list_head *free) +static int +rpcauth_cache_shrinker(int nr_to_scan, gfp_t gfp_mask) { - if (list_empty(&cred_unused) || time_before(jiffies, cache->nextgc)) - return; + LIST_HEAD(free); + int res; + + if (list_empty(&cred_unused)) + return 0; spin_lock(&rpc_credcache_lock); - cache->nextgc = jiffies + cache->expire; - rpcauth_prune_expired(free); + nr_to_scan = rpcauth_prune_expired(&free, nr_to_scan); + res = (number_cred_unused / 100) * sysctl_vfs_cache_pressure; spin_unlock(&rpc_credcache_lock); + rpcauth_destroy_credlist(&free); + return res; } /* @@ -318,7 +328,6 @@ found: cred = ERR_PTR(res); } } - rpcauth_gc_credcache(cache, &free); rpcauth_destroy_credlist(&free); out: return cred; @@ -408,13 +417,16 @@ put_rpccred(struct rpc_cred *cred) need_lock: if (!atomic_dec_and_lock(&cred->cr_count, &rpc_credcache_lock)) return; - if (!list_empty(&cred->cr_lru)) + if (!list_empty(&cred->cr_lru)) { + number_cred_unused--; list_del_init(&cred->cr_lru); + } if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0) rpcauth_unhash_cred(cred); else if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0) { cred->cr_expire = jiffies; list_add_tail(&cred->cr_lru, &cred_unused); + number_cred_unused++; spin_unlock(&rpc_credcache_lock); return; } @@ -520,3 +532,18 @@ rpcauth_uptodatecred(struct rpc_task *task) return cred == NULL || test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0; } + + +static struct shrinker *rpc_cred_shrinker; + +void __init rpcauth_init_module(void) +{ + rpc_init_authunix(); + rpc_cred_shrinker = set_shrinker(DEFAULT_SEEKS, rpcauth_cache_shrinker); +} + +void __exit rpcauth_remove_module(void) +{ + if (rpc_cred_shrinker != NULL) + remove_shrinker(rpc_cred_shrinker); +} diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 068fa6d..8653a92 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -64,7 +64,6 @@ static const struct rpc_credops gss_credops; #define NFS_NGROUPS 16 -#define GSS_CRED_EXPIRE (60 * HZ) /* XXX: reasonable? */ #define GSS_CRED_SLACK 1024 /* XXX: unused */ /* length of a krb5 verifier (48), plus data added before arguments when * using integrity (two 4-byte integers): */ @@ -643,7 +642,7 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) goto err_put_mech; } - err = rpcauth_init_credcache(auth, GSS_CRED_EXPIRE); + err = rpcauth_init_credcache(auth); if (err) goto err_unlink_pipe; diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 205878a..d9c50d8 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -21,8 +21,6 @@ struct unx_cred { }; #define uc_uid uc_base.cr_uid -#define UNX_CRED_EXPIRE (60 * HZ) - #define UNX_WRITESLACK (21 + (UNX_MAXNODENAME >> 2)) #ifdef RPC_DEBUG @@ -38,8 +36,7 @@ unx_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) { dprintk("RPC: creating UNIX authenticator for client %p\n", clnt); - if (atomic_inc_return(&unix_auth.au_count) == 1) - unix_cred_cache.nextgc = jiffies + (unix_cred_cache.expire >> 1); + atomic_inc(&unix_auth.au_count); return &unix_auth; } @@ -232,7 +229,6 @@ const struct rpc_authops authunix_ops = { static struct rpc_cred_cache unix_cred_cache = { - .expire = UNX_CRED_EXPIRE, }; static diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 018065f..384c4ad 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -152,7 +152,7 @@ init_sunrpc(void) cache_register(&ip_map_cache); cache_register(&unix_gid_cache); init_socket_xprt(); - rpc_init_authunix(); + rpcauth_init_module(); out: return err; } @@ -160,6 +160,7 @@ out: static void __exit cleanup_sunrpc(void) { + rpcauth_remove_module(); cleanup_socket_xprt(); unregister_rpc_pipefs(); rpc_destroy_mempool(); -- cgit v1.1 From 5d28dc82074f1e64b22c9424b161abc1f5d6bcdb Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 26 Jun 2007 19:18:38 -0400 Subject: SUNRPC: Convert gss_ctx_lock to an RCU lock Signed-off-by: Trond Myklebust --- net/sunrpc/auth_gss/auth_gss.c | 53 ++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 8653a92..15da6f8 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -78,8 +78,6 @@ static const struct rpc_credops gss_credops; /* dump the buffer in `emacs-hexl' style */ #define isprint(c) ((c > 0x1f) && (c < 0x7f)) -static DEFINE_RWLOCK(gss_ctx_lock); - struct gss_auth { struct rpc_auth rpc_auth; struct gss_api_mech *mech; @@ -88,7 +86,7 @@ struct gss_auth { struct dentry *dentry; }; -static void gss_destroy_ctx(struct gss_cl_ctx *); +static void gss_free_ctx(struct gss_cl_ctx *); static struct rpc_pipe_ops gss_upcall_ops; static inline struct gss_cl_ctx * @@ -102,20 +100,24 @@ static inline void gss_put_ctx(struct gss_cl_ctx *ctx) { if (atomic_dec_and_test(&ctx->count)) - gss_destroy_ctx(ctx); + gss_free_ctx(ctx); } +/* gss_cred_set_ctx: + * called by gss_upcall_callback and gss_create_upcall in order + * to set the gss context. The actual exchange of an old context + * and a new one is protected by the inode->i_lock. + */ static void gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx) { struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_cl_ctx *old; - write_lock(&gss_ctx_lock); + old = gss_cred->gc_ctx; - gss_cred->gc_ctx = ctx; + rcu_assign_pointer(gss_cred->gc_ctx, ctx); set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags); - write_unlock(&gss_ctx_lock); if (old) gss_put_ctx(old); } @@ -126,10 +128,10 @@ gss_cred_is_uptodate_ctx(struct rpc_cred *cred) struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); int res = 0; - read_lock(&gss_ctx_lock); + rcu_read_lock(); if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) && gss_cred->gc_ctx) res = 1; - read_unlock(&gss_ctx_lock); + rcu_read_unlock(); return res; } @@ -168,10 +170,10 @@ gss_cred_get_ctx(struct rpc_cred *cred) struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_cl_ctx *ctx = NULL; - read_lock(&gss_ctx_lock); + rcu_read_lock(); if (gss_cred->gc_ctx) ctx = gss_get_ctx(gss_cred->gc_ctx); - read_unlock(&gss_ctx_lock); + rcu_read_unlock(); return ctx; } @@ -333,11 +335,11 @@ gss_upcall_callback(struct rpc_task *task) struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall; struct inode *inode = gss_msg->auth->dentry->d_inode; + spin_lock(&inode->i_lock); if (gss_msg->ctx) gss_cred_set_ctx(task->tk_msg.rpc_cred, gss_get_ctx(gss_msg->ctx)); else task->tk_status = gss_msg->msg.errno; - spin_lock(&inode->i_lock); gss_cred->gc_upcall = NULL; rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); spin_unlock(&inode->i_lock); @@ -440,7 +442,6 @@ gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_INTERRUPTIBLE); spin_lock(&inode->i_lock); if (gss_msg->ctx != NULL || gss_msg->msg.errno < 0) { - spin_unlock(&inode->i_lock); break; } spin_unlock(&inode->i_lock); @@ -454,6 +455,7 @@ gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) gss_cred_set_ctx(cred, gss_get_ctx(gss_msg->ctx)); else err = gss_msg->msg.errno; + spin_unlock(&inode->i_lock); out_intr: finish_wait(&gss_msg->waitqueue, &wait); gss_release_msg(gss_msg); @@ -681,9 +683,9 @@ gss_destroy(struct rpc_auth *auth) * to create a new cred or context, so they check that things have been * allocated before freeing them. */ static void -gss_destroy_ctx(struct gss_cl_ctx *ctx) +gss_do_free_ctx(struct gss_cl_ctx *ctx) { - dprintk("RPC: gss_destroy_ctx\n"); + dprintk("RPC: gss_free_ctx\n"); if (ctx->gc_gss_ctx) gss_delete_sec_context(&ctx->gc_gss_ctx); @@ -693,11 +695,22 @@ gss_destroy_ctx(struct gss_cl_ctx *ctx) } static void +gss_free_ctx_callback(struct rcu_head *head) +{ + struct gss_cl_ctx *ctx = container_of(head, struct gss_cl_ctx, gc_rcu); + gss_do_free_ctx(ctx); +} + +static void +gss_free_ctx(struct gss_cl_ctx *ctx) +{ + call_rcu(&ctx->gc_rcu, gss_free_ctx_callback); +} + +static void gss_free_cred(struct gss_cred *gss_cred) { dprintk("RPC: gss_free_cred %p\n", gss_cred); - if (gss_cred->gc_ctx) - gss_put_ctx(gss_cred->gc_ctx); kfree(gss_cred); } @@ -711,7 +724,13 @@ gss_free_cred_callback(struct rcu_head *head) static void gss_destroy_cred(struct rpc_cred *cred) { + struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); + struct gss_cl_ctx *ctx = gss_cred->gc_ctx; + + rcu_assign_pointer(gss_cred->gc_ctx, NULL); call_rcu(&cred->cr_rcu, gss_free_cred_callback); + if (ctx) + gss_put_ctx(ctx); } /* -- cgit v1.1 From 1dd17ec693bf4a08b666c2ef76b68ca08ce3c93d Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 26 Jun 2007 16:57:41 -0400 Subject: SUNRPC: Allow rpc_auth to run clean up before the rpc_client is destroyed RPCSEC_GSS needs to be able to send NULL RPC calls to the server in order to free up any remaining GSS contexts. Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 98df44e..28a7894 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -352,10 +352,6 @@ rpc_free_client(struct kref *kref) dprintk("RPC: destroying %s client for %s\n", clnt->cl_protname, clnt->cl_server); - if (clnt->cl_auth) { - rpcauth_release(clnt->cl_auth); - clnt->cl_auth = NULL; - } if (!IS_ERR(clnt->cl_dentry)) { rpc_rmdir(clnt->cl_dentry); rpc_put_mount(); @@ -376,6 +372,30 @@ out_free: } /* + * Free an RPC client + */ +static void +rpc_free_auth(struct kref *kref) +{ + struct rpc_clnt *clnt = container_of(kref, struct rpc_clnt, cl_kref); + + if (clnt->cl_auth == NULL) { + rpc_free_client(kref); + return; + } + + /* + * Note: RPCSEC_GSS may need to send NULL RPC calls in order to + * release remaining GSS contexts. This mechanism ensures + * that it can do so safely. + */ + kref_init(kref); + rpcauth_release(clnt->cl_auth); + clnt->cl_auth = NULL; + kref_put(kref, rpc_free_client); +} + +/* * Release reference to the RPC client */ void @@ -385,7 +405,7 @@ rpc_release_client(struct rpc_clnt *clnt) if (list_empty(&clnt->cl_tasks)) wake_up(&destroy_wait); - kref_put(&clnt->cl_kref, rpc_free_client); + kref_put(&clnt->cl_kref, rpc_free_auth); } /** -- cgit v1.1 From 1be27f36601973815171db684c711d30557cf50c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 27 Jun 2007 14:29:04 -0400 Subject: SUNRPC: Remove the tk_auth macro... We should almost always be deferencing the rpc_auth struct by means of the credential's cr_auth field instead of the rpc_clnt->cl_auth anyway. Fix up that historical mistake, and remove the macro that propagated it. Signed-off-by: Trond Myklebust --- net/sunrpc/auth.c | 25 +++++++++++++------------ net/sunrpc/auth_gss/auth_gss.c | 4 ++-- net/sunrpc/auth_unix.c | 2 +- net/sunrpc/clnt.c | 2 +- 4 files changed, 17 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 81f4c77..74baf87 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -371,7 +371,7 @@ EXPORT_SYMBOL(rpcauth_init_cred); struct rpc_cred * rpcauth_bindcred(struct rpc_task *task) { - struct rpc_auth *auth = task->tk_auth; + struct rpc_auth *auth = task->tk_client->cl_auth; struct auth_cred acred = { .uid = current->fsuid, .gid = current->fsgid, @@ -381,7 +381,7 @@ rpcauth_bindcred(struct rpc_task *task) int flags = 0; dprintk("RPC: %5u looking up %s cred\n", - task->tk_pid, task->tk_auth->au_ops->au_name); + task->tk_pid, task->tk_client->cl_auth->au_ops->au_name); get_group_info(acred.group_info); if (task->tk_flags & RPC_TASK_ROOTCREDS) flags |= RPCAUTH_LOOKUP_ROOTCREDS; @@ -397,11 +397,12 @@ rpcauth_bindcred(struct rpc_task *task) void rpcauth_holdcred(struct rpc_task *task) { - dprintk("RPC: %5u holding %s cred %p\n", - task->tk_pid, task->tk_auth->au_ops->au_name, - task->tk_msg.rpc_cred); - if (task->tk_msg.rpc_cred) - get_rpccred(task->tk_msg.rpc_cred); + struct rpc_cred *cred = task->tk_msg.rpc_cred; + if (cred != NULL) { + get_rpccred(cred); + dprintk("RPC: %5u holding %s cred %p\n", task->tk_pid, + cred->cr_auth->au_ops->au_name, cred); + } } void @@ -441,7 +442,7 @@ rpcauth_unbindcred(struct rpc_task *task) struct rpc_cred *cred = task->tk_msg.rpc_cred; dprintk("RPC: %5u releasing %s cred %p\n", - task->tk_pid, task->tk_auth->au_ops->au_name, cred); + task->tk_pid, cred->cr_auth->au_ops->au_name, cred); put_rpccred(cred); task->tk_msg.rpc_cred = NULL; @@ -453,7 +454,7 @@ rpcauth_marshcred(struct rpc_task *task, __be32 *p) struct rpc_cred *cred = task->tk_msg.rpc_cred; dprintk("RPC: %5u marshaling %s cred %p\n", - task->tk_pid, task->tk_auth->au_ops->au_name, cred); + task->tk_pid, cred->cr_auth->au_ops->au_name, cred); return cred->cr_ops->crmarshal(task, p); } @@ -464,7 +465,7 @@ rpcauth_checkverf(struct rpc_task *task, __be32 *p) struct rpc_cred *cred = task->tk_msg.rpc_cred; dprintk("RPC: %5u validating %s cred %p\n", - task->tk_pid, task->tk_auth->au_ops->au_name, cred); + task->tk_pid, cred->cr_auth->au_ops->au_name, cred); return cred->cr_ops->crvalidate(task, p); } @@ -505,7 +506,7 @@ rpcauth_refreshcred(struct rpc_task *task) int err; dprintk("RPC: %5u refreshing %s cred %p\n", - task->tk_pid, task->tk_auth->au_ops->au_name, cred); + task->tk_pid, cred->cr_auth->au_ops->au_name, cred); err = cred->cr_ops->crrefresh(task); if (err < 0) @@ -519,7 +520,7 @@ rpcauth_invalcred(struct rpc_task *task) struct rpc_cred *cred = task->tk_msg.rpc_cred; dprintk("RPC: %5u invalidating %s cred %p\n", - task->tk_pid, task->tk_auth->au_ops->au_name, cred); + task->tk_pid, cred->cr_auth->au_ops->au_name, cred); if (cred) clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); } diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 15da6f8..debcda8 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -906,7 +906,7 @@ gss_validate(struct rpc_task *task, __be32 *p) goto out_bad; /* We leave it to unwrap to calculate au_rslack. For now we just * calculate the length of the verifier: */ - task->tk_auth->au_verfsize = XDR_QUADLEN(len) + 2; + cred->cr_auth->au_verfsize = XDR_QUADLEN(len) + 2; gss_put_ctx(ctx); dprintk("RPC: %5u gss_validate: gss_verify_mic succeeded.\n", task->tk_pid); @@ -1206,7 +1206,7 @@ gss_unwrap_resp(struct rpc_task *task, break; } /* take into account extra slack for integrity and privacy cases: */ - task->tk_auth->au_rslack = task->tk_auth->au_verfsize + (p - savedp) + cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + (p - savedp) + (savedlen - head->iov_len); out_decode: status = decode(rqstp, p, obj); diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index d9c50d8..5ed91e5 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -204,7 +204,7 @@ unx_validate(struct rpc_task *task, __be32 *p) printk("RPC: giant verf size: %u\n", size); return NULL; } - task->tk_auth->au_rslack = (size >> 2) + 2; + task->tk_msg.rpc_cred->cr_auth->au_rslack = (size >> 2) + 2; p += (size >> 2); return p; diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 28a7894..50af8bb 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -787,7 +787,7 @@ call_reserveresult(struct rpc_task *task) static void call_allocate(struct rpc_task *task) { - unsigned int slack = task->tk_auth->au_cslack; + unsigned int slack = task->tk_msg.rpc_cred->cr_auth->au_cslack; struct rpc_rqst *req = task->tk_rqstp; struct rpc_xprt *xprt = task->tk_xprt; struct rpc_procinfo *proc = task->tk_msg.rpc_proc; -- cgit v1.1 From 0285ed1f12298e5304f0f2642e2cf31a5f302e61 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 27 Jun 2007 14:29:12 -0400 Subject: SUNRPC: Ensure that the struct gss_auth lifetime exceeds the credential's Add a refcount in order to ensure that the gss_auth doesn't disappear from underneath us while we're freeing up GSS contexts. Signed-off-by: Trond Myklebust --- net/sunrpc/auth_gss/auth_gss.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index debcda8..982aba6 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -79,6 +79,7 @@ static const struct rpc_credops gss_credops; #define isprint(c) ((c > 0x1f) && (c < 0x7f)) struct gss_auth { + struct kref kref; struct rpc_auth rpc_auth; struct gss_api_mech *mech; enum rpc_gss_svc service; @@ -636,6 +637,7 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) auth->au_ops = &authgss_ops; auth->au_flavor = flavor; atomic_set(&auth->au_count, 1); + kref_init(&gss_auth->kref); gss_auth->dentry = rpc_mkpipe(clnt->cl_dentry, gss_auth->mech->gm_name, clnt, &gss_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN); @@ -661,6 +663,25 @@ out_dec: } static void +gss_free(struct gss_auth *gss_auth) +{ + rpc_unlink(gss_auth->dentry); + gss_auth->dentry = NULL; + gss_mech_put(gss_auth->mech); + + kfree(gss_auth); + module_put(THIS_MODULE); +} + +static void +gss_free_callback(struct kref *kref) +{ + struct gss_auth *gss_auth = container_of(kref, struct gss_auth, kref); + + gss_free(gss_auth); +} + +static void gss_destroy(struct rpc_auth *auth) { struct gss_auth *gss_auth; @@ -671,12 +692,7 @@ gss_destroy(struct rpc_auth *auth) rpcauth_destroy_credcache(auth); gss_auth = container_of(auth, struct gss_auth, rpc_auth); - rpc_unlink(gss_auth->dentry); - gss_auth->dentry = NULL; - gss_mech_put(gss_auth->mech); - - kfree(gss_auth); - module_put(THIS_MODULE); + kref_put(&gss_auth->kref, gss_free_callback); } /* gss_destroy_cred (and gss_destroy_ctx) are used to clean up after failure @@ -725,12 +741,14 @@ static void gss_destroy_cred(struct rpc_cred *cred) { struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); + struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); struct gss_cl_ctx *ctx = gss_cred->gc_ctx; rcu_assign_pointer(gss_cred->gc_ctx, NULL); call_rcu(&cred->cr_rcu, gss_free_cred_callback); if (ctx) gss_put_ctx(ctx); + kref_put(&gss_auth->kref, gss_free_callback); } /* @@ -762,6 +780,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; + kref_get(&gss_auth->kref); return &cred->gc_base; out_err: -- cgit v1.1 From 0df7fb74fbb709591301871a38aac7735a1d6583 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 26 Jun 2007 17:04:57 -0400 Subject: SUNRPC: Ensure RPCSEC_GSS destroys the security context when freeing a cred Do so by set the gc_proc field to RPC_GSS_PROC_DESTROY, and then sending a NULL RPC call. Signed-off-by: Trond Myklebust --- net/sunrpc/auth_gss/auth_gss.c | 60 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 982aba6..17d460f 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -57,6 +57,7 @@ static const struct rpc_authops authgss_ops; static const struct rpc_credops gss_credops; +static const struct rpc_credops gss_nullops; #ifdef RPC_DEBUG # define RPCDBG_FACILITY RPCDBG_AUTH @@ -695,7 +696,39 @@ gss_destroy(struct rpc_auth *auth) kref_put(&gss_auth->kref, gss_free_callback); } -/* gss_destroy_cred (and gss_destroy_ctx) are used to clean up after failure +/* + * gss_destroying_context will cause the RPCSEC_GSS to send a NULL RPC call + * to the server with the GSS control procedure field set to + * RPC_GSS_PROC_DESTROY. This should normally cause the server to release + * all RPCSEC_GSS state associated with that context. + */ +static int +gss_destroying_context(struct rpc_cred *cred) +{ + struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); + struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); + struct rpc_task *task; + + if (gss_cred->gc_ctx == NULL || + gss_cred->gc_ctx->gc_proc == RPC_GSS_PROC_DESTROY) + return 0; + + gss_cred->gc_ctx->gc_proc = RPC_GSS_PROC_DESTROY; + cred->cr_ops = &gss_nullops; + + /* Take a reference to ensure the cred will be destroyed either + * by the RPC call or by the put_rpccred() below */ + get_rpccred(cred); + + task = rpc_call_null(gss_auth->client, cred, RPC_TASK_ASYNC); + if (!IS_ERR(task)) + rpc_put_task(task); + + put_rpccred(cred); + return 1; +} + +/* gss_destroy_cred (and gss_free_ctx) are used to clean up after failure * to create a new cred or context, so they check that things have been * allocated before freeing them. */ static void @@ -744,6 +777,8 @@ gss_destroy_cred(struct rpc_cred *cred) struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); struct gss_cl_ctx *ctx = gss_cred->gc_ctx; + if (gss_destroying_context(cred)) + return; rcu_assign_pointer(gss_cred->gc_ctx, NULL); call_rcu(&cred->cr_rcu, gss_free_cred_callback); if (ctx) @@ -892,6 +927,13 @@ gss_refresh(struct rpc_task *task) return 0; } +/* Dummy refresh routine: used only when destroying the context */ +static int +gss_refresh_null(struct rpc_task *task) +{ + return -EACCES; +} + static __be32 * gss_validate(struct rpc_task *task, __be32 *p) { @@ -921,8 +963,11 @@ gss_validate(struct rpc_task *task, __be32 *p) maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic); if (maj_stat == GSS_S_CONTEXT_EXPIRED) clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); - if (maj_stat) + if (maj_stat) { + dprintk("RPC: %5u gss_validate: gss_verify_mic returned" + "error 0x%08x\n", task->tk_pid, maj_stat); goto out_bad; + } /* We leave it to unwrap to calculate au_rslack. For now we just * calculate the length of the verifier: */ cred->cr_auth->au_verfsize = XDR_QUADLEN(len) + 2; @@ -1260,6 +1305,17 @@ static const struct rpc_credops gss_credops = { .crunwrap_resp = gss_unwrap_resp, }; +static const struct rpc_credops gss_nullops = { + .cr_name = "AUTH_GSS", + .crdestroy = gss_destroy_cred, + .crmatch = gss_match, + .crmarshal = gss_marshal, + .crrefresh = gss_refresh_null, + .crvalidate = gss_validate, + .crwrap_req = gss_wrap_req, + .crunwrap_resp = gss_unwrap_resp, +}; + static struct rpc_pipe_ops gss_upcall_ops = { .upcall = gss_pipe_upcall, .downcall = gss_pipe_downcall, -- cgit v1.1 From 8a702bbb7ddaa2e78c17dbaaf48e3cd5943676f0 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 27 Jun 2007 18:30:26 -0400 Subject: SUNRPC: Suppress some noisy and unnecessary printk() calls in call_verify() Convert them into dprintk() calls. Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 50af8bb..e1553cf 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1315,9 +1315,9 @@ call_verify(struct rpc_task *task) * - if it isn't pointer subtraction in the NFS client may give * undefined results */ - printk(KERN_WARNING - "call_verify: XDR representation not a multiple of" - " 4 bytes: 0x%x\n", task->tk_rqstp->rq_rcv_buf.len); + dprintk("RPC: %5u %s: XDR representation not a multiple of" + " 4 bytes: 0x%x\n", task->tk_pid, __FUNCTION__, + task->tk_rqstp->rq_rcv_buf.len); goto out_eio; } if ((len -= 3) < 0) @@ -1325,7 +1325,8 @@ call_verify(struct rpc_task *task) p += 1; /* skip XID */ if ((n = ntohl(*p++)) != RPC_REPLY) { - printk(KERN_WARNING "call_verify: not an RPC reply: %x\n", n); + dprintk("RPC: %5u %s: not an RPC reply: %x\n", + task->tk_pid, __FUNCTION__, n); goto out_garbage; } if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) { @@ -1376,7 +1377,8 @@ call_verify(struct rpc_task *task) "authentication.\n", task->tk_client->cl_server); break; default: - printk(KERN_WARNING "call_verify: unknown auth error: %x\n", n); + dprintk("RPC: %5u %s: unknown auth error: %x\n", + task->tk_pid, __FUNCTION__, n); error = -EIO; } dprintk("RPC: %5u %s: call rejected %d\n", @@ -1384,7 +1386,8 @@ call_verify(struct rpc_task *task) goto out_err; } if (!(p = rpcauth_checkverf(task, p))) { - printk(KERN_WARNING "call_verify: auth check failed\n"); + dprintk("RPC: %5u %s: auth check failed\n", + task->tk_pid, __FUNCTION__); goto out_garbage; /* bad verifier, retry */ } len = p - (__be32 *)iov->iov_base - 1; @@ -1423,7 +1426,8 @@ call_verify(struct rpc_task *task) task->tk_pid, __FUNCTION__); break; /* retry */ default: - printk(KERN_WARNING "call_verify: server accept status: %x\n", n); + dprintk("RPC: %5u %s: server accept status: %x\n", + task->tk_pid, __FUNCTION__, n); /* Also retry */ } @@ -1437,14 +1441,16 @@ out_garbage: out_retry: return ERR_PTR(-EAGAIN); } - printk(KERN_WARNING "RPC %s: retry failed, exit EIO\n", __FUNCTION__); out_eio: error = -EIO; out_err: rpc_exit(task, error); + dprintk("RPC: %5u %s: call failed with error %d\n", task->tk_pid, + __FUNCTION__, error); return ERR_PTR(error); out_overflow: - printk(KERN_WARNING "RPC %s: server reply was truncated.\n", __FUNCTION__); + dprintk("RPC: %5u %s: server reply was truncated.\n", task->tk_pid, + __FUNCTION__); goto out_garbage; } -- cgit v1.1 From f7fb558e503dc80e100acaf116f7261cdd97f0ca Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Sun, 1 Jul 2007 12:13:07 -0400 Subject: SUNRPC: Allow rpcbind requests to be interrupted by a signal. This allows NFS mount requests and RPC re-binding to be interruptible if the server isn't responding. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- net/sunrpc/rpcb_clnt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index cf7db59..9a20f38 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -184,7 +184,8 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr, .program = &rpcb_program, .version = version, .authflavor = RPC_AUTH_UNIX, - .flags = RPC_CLNT_CREATE_NOPING, + .flags = (RPC_CLNT_CREATE_NOPING | + RPC_CLNT_CREATE_INTR), }; ((struct sockaddr_in *)srvaddr)->sin_port = htons(RPCBIND_PORT); -- cgit v1.1 From cce63cd6374e6f1b4ea897ece1454feb13993d7c Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Sun, 1 Jul 2007 12:13:12 -0400 Subject: SUNRPC: Rename rpcb_getport_external routine In preparation for handling NFS mount option parsing in the kernel, rename rpcb_getport_external as rpcb_get_port_sync, and make it available always (instead of only when CONFIG_ROOT_NFS is enabled). Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- net/sunrpc/rpcb_clnt.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 9a20f38..fc881a6 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -12,6 +12,8 @@ * Copyright (C) 1996, Olaf Kirch */ +#include + #include #include #include @@ -247,21 +249,20 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) return error; } -#ifdef CONFIG_ROOT_NFS /** - * rpcb_getport_external - obtain the port for an RPC service on a given host + * rpcb_getport_sync - obtain the port for an RPC service on a given host * @sin: address of remote peer * @prog: RPC program number to bind * @vers: RPC version number to bind * @prot: transport protocol to use to make this request * * Called from outside the RPC client in a synchronous task context. + * Uses default timeout parameters specified by underlying transport. * - * For now, this supports only version 2 queries, but is used only by - * mount_clnt for NFS_ROOT. + * XXX: Needs to support IPv6, and rpcbind versions 3 and 4 */ -int rpcb_getport_external(struct sockaddr_in *sin, __u32 prog, - __u32 vers, int prot) +int rpcb_getport_sync(struct sockaddr_in *sin, __u32 prog, + __u32 vers, int prot) { struct rpcbind_args map = { .r_prog = prog, @@ -278,10 +279,10 @@ int rpcb_getport_external(struct sockaddr_in *sin, __u32 prog, char hostname[40]; int status; - dprintk("RPC: rpcb_getport_external(%u.%u.%u.%u, %u, %u, %d)\n", - NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot); + dprintk("RPC: %s(" NIPQUAD_FMT ", %u, %u, %d)\n", + __FUNCTION__, NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot); - sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(sin->sin_addr.s_addr)); + sprintf(hostname, NIPQUAD_FMT, NIPQUAD(sin->sin_addr.s_addr)); rpcb_clnt = rpcb_create(hostname, (struct sockaddr *)sin, prot, 2, 0); if (IS_ERR(rpcb_clnt)) return PTR_ERR(rpcb_clnt); @@ -296,7 +297,7 @@ int rpcb_getport_external(struct sockaddr_in *sin, __u32 prog, } return status; } -#endif +EXPORT_SYMBOL_GPL(rpcb_getport_sync); /** * rpcb_getport - obtain the port for a given RPC service on a given host -- cgit v1.1 From 45160d6275814e0c86206e6981f0b92c61a50a21 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Sun, 1 Jul 2007 12:13:17 -0400 Subject: SUNRPC: Rename rpcb_getport to be consistent with new rpcb_getport_sync name Clean up, for consistency. Rename rpcb_getport as rpcb_getport_async, to match the naming scheme of rpcb_getport_sync. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- net/sunrpc/rpcb_clnt.c | 37 +++++++++++++++++++------------------ net/sunrpc/xprtsock.c | 4 ++-- 2 files changed, 21 insertions(+), 20 deletions(-) (limited to 'net') diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index fc881a6..d1740db 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -300,13 +300,13 @@ int rpcb_getport_sync(struct sockaddr_in *sin, __u32 prog, EXPORT_SYMBOL_GPL(rpcb_getport_sync); /** - * rpcb_getport - obtain the port for a given RPC service on a given host + * rpcb_getport_async - obtain the port for a given RPC service on a given host * @task: task that is waiting for portmapper request * * This one can be called for an ongoing RPC request, and can be used in * an async (rpciod) context. */ -void rpcb_getport(struct rpc_task *task) +void rpcb_getport_async(struct rpc_task *task) { struct rpc_clnt *clnt = task->tk_client; int bind_version; @@ -317,17 +317,17 @@ void rpcb_getport(struct rpc_task *task) struct sockaddr addr; int status; - dprintk("RPC: %5u rpcb_getport(%s, %u, %u, %d)\n", - task->tk_pid, clnt->cl_server, - clnt->cl_prog, clnt->cl_vers, xprt->prot); + dprintk("RPC: %5u %s(%s, %u, %u, %d)\n", + task->tk_pid, __FUNCTION__, + clnt->cl_server, clnt->cl_prog, clnt->cl_vers, xprt->prot); /* Autobind on cloned rpc clients is discouraged */ BUG_ON(clnt->cl_parent != clnt); if (xprt_test_and_set_binding(xprt)) { status = -EACCES; /* tell caller to check again */ - dprintk("RPC: %5u rpcb_getport waiting for another binder\n", - task->tk_pid); + dprintk("RPC: %5u %s: waiting for another binder\n", + task->tk_pid, __FUNCTION__); goto bailout_nowake; } @@ -338,27 +338,28 @@ void rpcb_getport(struct rpc_task *task) /* Someone else may have bound if we slept */ if (xprt_bound(xprt)) { status = 0; - dprintk("RPC: %5u rpcb_getport already bound\n", task->tk_pid); + dprintk("RPC: %5u %s: already bound\n", + task->tk_pid, __FUNCTION__); goto bailout_nofree; } if (rpcb_next_version[xprt->bind_index].rpc_proc == NULL) { xprt->bind_index = 0; status = -EACCES; /* tell caller to try again later */ - dprintk("RPC: %5u rpcb_getport no more getport versions " - "available\n", task->tk_pid); + dprintk("RPC: %5u %s: no more getport versions available\n", + task->tk_pid, __FUNCTION__); goto bailout_nofree; } bind_version = rpcb_next_version[xprt->bind_index].rpc_vers; - dprintk("RPC: %5u rpcb_getport trying rpcbind version %u\n", - task->tk_pid, bind_version); + dprintk("RPC: %5u %s: trying rpcbind version %u\n", + task->tk_pid, __FUNCTION__, bind_version); map = kzalloc(sizeof(struct rpcbind_args), GFP_ATOMIC); if (!map) { status = -ENOMEM; - dprintk("RPC: %5u rpcb_getport no memory available\n", - task->tk_pid); + dprintk("RPC: %5u %s: no memory available\n", + task->tk_pid, __FUNCTION__); goto bailout_nofree; } map->r_prog = clnt->cl_prog; @@ -376,8 +377,8 @@ void rpcb_getport(struct rpc_task *task) rpcb_clnt = rpcb_create(clnt->cl_server, &addr, xprt->prot, bind_version, 0); if (IS_ERR(rpcb_clnt)) { status = PTR_ERR(rpcb_clnt); - dprintk("RPC: %5u rpcb_getport rpcb_create failed, error %ld\n", - task->tk_pid, PTR_ERR(rpcb_clnt)); + dprintk("RPC: %5u %s: rpcb_create failed, error %ld\n", + task->tk_pid, __FUNCTION__, PTR_ERR(rpcb_clnt)); goto bailout; } @@ -385,8 +386,8 @@ void rpcb_getport(struct rpc_task *task) rpc_release_client(rpcb_clnt); if (IS_ERR(child)) { status = -EIO; - dprintk("RPC: %5u rpcb_getport rpc_run_task failed\n", - task->tk_pid); + dprintk("RPC: %5u %s: rpc_run_task failed\n", + task->tk_pid, __FUNCTION__); goto bailout_nofree; } rpc_put_task(child); diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index ee6ad3b..a8f7c5f 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -1473,7 +1473,7 @@ static struct rpc_xprt_ops xs_udp_ops = { .set_buffer_size = xs_udp_set_buffer_size, .reserve_xprt = xprt_reserve_xprt_cong, .release_xprt = xprt_release_xprt_cong, - .rpcbind = rpcb_getport, + .rpcbind = rpcb_getport_async, .set_port = xs_set_port, .connect = xs_connect, .buf_alloc = rpc_malloc, @@ -1490,7 +1490,7 @@ static struct rpc_xprt_ops xs_udp_ops = { static struct rpc_xprt_ops xs_tcp_ops = { .reserve_xprt = xprt_reserve_xprt, .release_xprt = xs_tcp_release_xprt, - .rpcbind = rpcb_getport, + .rpcbind = rpcb_getport_async, .set_port = xs_set_port, .connect = xs_connect, .buf_alloc = rpc_malloc, -- cgit v1.1 From 43780b87fa799ae65df11d89d4539d8d6a7c67eb Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Sun, 1 Jul 2007 12:13:22 -0400 Subject: SUNRPC: Add a convenient default for the hostname when calling rpc_create() A couple of callers just use a stringified IP address for the rpc client's hostname. Move the logic for constructing this into rpc_create(), so it can be shared. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index e1553cf..0d9b527 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -234,6 +234,7 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args) { struct rpc_xprt *xprt; struct rpc_clnt *clnt; + char servername[20]; xprt = xprt_create_transport(args->protocol, args->address, args->addrsize, args->timeout); @@ -241,6 +242,18 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args) return (struct rpc_clnt *)xprt; /* + * If the caller chooses not to specify a hostname, whip + * up a string representation of the passed-in address. + */ + if (args->servername == NULL) { + struct sockaddr_in *addr = + (struct sockaddr_in *) &args->address; + snprintf(servername, sizeof(servername), NIPQUAD_FMT, + NIPQUAD(addr->sin_addr.s_addr)); + args->servername = servername; + } + + /* * By default, kernel RPC client connects from a reserved port. * CAP_NET_BIND_SERVICE will not be set for unprivileged requesters, * but it is always enabled for rpciod, which handles the connect -- cgit v1.1 From 96802a095171f5b35cf0e1e0d4be943e6696a253 Mon Sep 17 00:00:00 2001 From: Frank van Maarseveen Date: Sun, 8 Jul 2007 13:08:54 +0200 Subject: SUNRPC: cleanup transport creation argument passing Cleanup argument passing to functions for creating an RPC transport. Signed-off-by: Frank van Maarseveen Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 9 +++++++-- net/sunrpc/xprt.c | 15 ++++++--------- net/sunrpc/xprtsock.c | 36 ++++++++++++++++-------------------- 3 files changed, 29 insertions(+), 31 deletions(-) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 0d9b527..25bbf2d 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -234,10 +234,15 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args) { struct rpc_xprt *xprt; struct rpc_clnt *clnt; + struct rpc_xprtsock_create xprtargs = { + .proto = args->protocol, + .dstaddr = args->address, + .addrlen = args->addrsize, + .timeout = args->timeout + }; char servername[20]; - xprt = xprt_create_transport(args->protocol, args->address, - args->addrsize, args->timeout); + xprt = xprt_create_transport(&xprtargs); if (IS_ERR(xprt)) return (struct rpc_clnt *)xprt; diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 518acb7..c8c2edc 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -886,27 +886,24 @@ void xprt_set_timeout(struct rpc_timeout *to, unsigned int retr, unsigned long i /** * xprt_create_transport - create an RPC transport - * @proto: requested transport protocol - * @ap: remote peer address - * @size: length of address - * @to: timeout parameters + * @args: rpc transport creation arguments * */ -struct rpc_xprt *xprt_create_transport(int proto, struct sockaddr *ap, size_t size, struct rpc_timeout *to) +struct rpc_xprt *xprt_create_transport(struct rpc_xprtsock_create *args) { struct rpc_xprt *xprt; struct rpc_rqst *req; - switch (proto) { + switch (args->proto) { case IPPROTO_UDP: - xprt = xs_setup_udp(ap, size, to); + xprt = xs_setup_udp(args); break; case IPPROTO_TCP: - xprt = xs_setup_tcp(ap, size, to); + xprt = xs_setup_tcp(args); break; default: printk(KERN_ERR "RPC: unrecognized transport protocol: %d\n", - proto); + args->proto); return ERR_PTR(-EIO); } if (IS_ERR(xprt)) { diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index a8f7c5f..6b7cea5 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -1502,12 +1502,12 @@ static struct rpc_xprt_ops xs_tcp_ops = { .print_stats = xs_tcp_print_stats, }; -static struct rpc_xprt *xs_setup_xprt(struct sockaddr *addr, size_t addrlen, unsigned int slot_table_size) +static struct rpc_xprt *xs_setup_xprt(struct rpc_xprtsock_create *args, unsigned int slot_table_size) { struct rpc_xprt *xprt; struct sock_xprt *new; - if (addrlen > sizeof(xprt->addr)) { + if (args->addrlen > sizeof(xprt->addr)) { dprintk("RPC: xs_setup_xprt: address too large\n"); return ERR_PTR(-EBADF); } @@ -1529,8 +1529,8 @@ static struct rpc_xprt *xs_setup_xprt(struct sockaddr *addr, size_t addrlen, uns return ERR_PTR(-ENOMEM); } - memcpy(&xprt->addr, addr, addrlen); - xprt->addrlen = addrlen; + memcpy(&xprt->addr, args->dstaddr, args->addrlen); + xprt->addrlen = args->addrlen; new->port = xs_get_random_port(); return xprt; @@ -1538,22 +1538,20 @@ static struct rpc_xprt *xs_setup_xprt(struct sockaddr *addr, size_t addrlen, uns /** * xs_setup_udp - Set up transport to use a UDP socket - * @addr: address of remote server - * @addrlen: length of address in bytes - * @to: timeout parameters + * @args: rpc transport creation arguments * */ -struct rpc_xprt *xs_setup_udp(struct sockaddr *addr, size_t addrlen, struct rpc_timeout *to) +struct rpc_xprt *xs_setup_udp(struct rpc_xprtsock_create *args) { struct rpc_xprt *xprt; struct sock_xprt *transport; - xprt = xs_setup_xprt(addr, addrlen, xprt_udp_slot_table_entries); + xprt = xs_setup_xprt(args, xprt_udp_slot_table_entries); if (IS_ERR(xprt)) return xprt; transport = container_of(xprt, struct sock_xprt, xprt); - if (ntohs(((struct sockaddr_in *)addr)->sin_port) != 0) + if (ntohs(((struct sockaddr_in *)args->dstaddr)->sin_port) != 0) xprt_set_bound(xprt); xprt->prot = IPPROTO_UDP; @@ -1569,8 +1567,8 @@ struct rpc_xprt *xs_setup_udp(struct sockaddr *addr, size_t addrlen, struct rpc_ xprt->ops = &xs_udp_ops; - if (to) - xprt->timeout = *to; + if (args->timeout) + xprt->timeout = *args->timeout; else xprt_set_timeout(&xprt->timeout, 5, 5 * HZ); @@ -1583,22 +1581,20 @@ struct rpc_xprt *xs_setup_udp(struct sockaddr *addr, size_t addrlen, struct rpc_ /** * xs_setup_tcp - Set up transport to use a TCP socket - * @addr: address of remote server - * @addrlen: length of address in bytes - * @to: timeout parameters + * @args: rpc transport creation arguments * */ -struct rpc_xprt *xs_setup_tcp(struct sockaddr *addr, size_t addrlen, struct rpc_timeout *to) +struct rpc_xprt *xs_setup_tcp(struct rpc_xprtsock_create *args) { struct rpc_xprt *xprt; struct sock_xprt *transport; - xprt = xs_setup_xprt(addr, addrlen, xprt_tcp_slot_table_entries); + xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries); if (IS_ERR(xprt)) return xprt; transport = container_of(xprt, struct sock_xprt, xprt); - if (ntohs(((struct sockaddr_in *)addr)->sin_port) != 0) + if (ntohs(((struct sockaddr_in *)args->dstaddr)->sin_port) != 0) xprt_set_bound(xprt); xprt->prot = IPPROTO_TCP; @@ -1613,8 +1609,8 @@ struct rpc_xprt *xs_setup_tcp(struct sockaddr *addr, size_t addrlen, struct rpc_ xprt->ops = &xs_tcp_ops; - if (to) - xprt->timeout = *to; + if (args->timeout) + xprt->timeout = *args->timeout; else xprt_set_timeout(&xprt->timeout, 2, 60 * HZ); -- cgit v1.1 From a97476926ec061f90b77da478620ea6dc71a3237 Mon Sep 17 00:00:00 2001 From: Frank van Maarseveen Date: Mon, 9 Jul 2007 22:21:39 +0200 Subject: SUNRPC server: record the destination address of a request Save the destination address of an incoming request over TCP like is done already for UDP. It is necessary later for callbacks by the server. Signed-off-by: Frank van Maarseveen Signed-off-by: Trond Myklebust --- net/sunrpc/svcsock.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'net') diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 5baf48d..64b9b8c 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -644,6 +644,7 @@ svc_recvfrom(struct svc_rqst *rqstp, struct kvec *iov, int nr, int buflen) struct msghdr msg = { .msg_flags = MSG_DONTWAIT, }; + struct sockaddr *sin; int len; len = kernel_recvmsg(svsk->sk_sock, &msg, iov, nr, buflen, @@ -654,6 +655,19 @@ svc_recvfrom(struct svc_rqst *rqstp, struct kvec *iov, int nr, int buflen) memcpy(&rqstp->rq_addr, &svsk->sk_remote, svsk->sk_remotelen); rqstp->rq_addrlen = svsk->sk_remotelen; + /* Destination address in request is needed for binding the + * source address in RPC callbacks later. + */ + sin = (struct sockaddr *)&svsk->sk_local; + switch (sin->sa_family) { + case AF_INET: + rqstp->rq_daddr.addr = ((struct sockaddr_in *)sin)->sin_addr; + break; + case AF_INET6: + rqstp->rq_daddr.addr6 = ((struct sockaddr_in6 *)sin)->sin6_addr; + break; + } + dprintk("svc: socket %p recvfrom(%p, %Zu) = %d\n", svsk, iov[0].iov_base, iov[0].iov_len, len); @@ -1064,6 +1078,12 @@ svc_tcp_accept(struct svc_sock *svsk) goto failed; memcpy(&newsvsk->sk_remote, sin, slen); newsvsk->sk_remotelen = slen; + err = kernel_getsockname(newsock, sin, &slen); + if (unlikely(err < 0)) { + dprintk("svc_tcp_accept: kernel_getsockname error %d\n", -err); + slen = offsetof(struct sockaddr, sa_data); + } + memcpy(&newsvsk->sk_local, sin, slen); svc_sock_received(newsvsk); -- cgit v1.1 From d3bc9a1deb8964d774af8535814cb91bf8f6def0 Mon Sep 17 00:00:00 2001 From: Frank van Maarseveen Date: Mon, 9 Jul 2007 22:23:35 +0200 Subject: SUNRPC client: add interface for binding to a local address In addition to binding to a local privileged port the NFS client should allow binding to a specific local address. This is used by the server for callbacks. The patch adds the necessary interface. Signed-off-by: Frank van Maarseveen Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 1 + net/sunrpc/xprtsock.c | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 25bbf2d..5d3fe7b 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -236,6 +236,7 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args) struct rpc_clnt *clnt; struct rpc_xprtsock_create xprtargs = { .proto = args->protocol, + .srcaddr = args->saddress, .dstaddr = args->address, .addrlen = args->addrsize, .timeout = args->timeout diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 6b7cea5..4ae7eed 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -235,6 +235,7 @@ struct sock_xprt { * Connection of transports */ struct delayed_work connect_worker; + struct sockaddr_storage addr; unsigned short port; /* @@ -1145,31 +1146,36 @@ static void xs_set_port(struct rpc_xprt *xprt, unsigned short port) sap->sin_port = htons(port); } -static int xs_bindresvport(struct sock_xprt *transport, struct socket *sock) +static int xs_bind(struct sock_xprt *transport, struct socket *sock) { struct sockaddr_in myaddr = { .sin_family = AF_INET, }; + struct sockaddr_in *sa; int err; unsigned short port = transport->port; + if (!transport->xprt.resvport) + port = 0; + sa = (struct sockaddr_in *)&transport->addr; + myaddr.sin_addr = sa->sin_addr; do { myaddr.sin_port = htons(port); err = kernel_bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)); + if (!transport->xprt.resvport) + break; if (err == 0) { transport->port = port; - dprintk("RPC: xs_bindresvport bound to port %u\n", - port); - return 0; + break; } if (port <= xprt_min_resvport) port = xprt_max_resvport; else port--; } while (err == -EADDRINUSE && port != transport->port); - - dprintk("RPC: can't bind to reserved port (%d).\n", -err); + dprintk("RPC: xs_bind "NIPQUAD_FMT":%u: %s (%d)\n", + NIPQUAD(myaddr.sin_addr), port, err ? "failed" : "ok", err); return err; } @@ -1228,7 +1234,7 @@ static void xs_udp_connect_worker(struct work_struct *work) } xs_reclassify_socket(sock); - if (xprt->resvport && xs_bindresvport(transport, sock) < 0) { + if (xs_bind(transport, sock)) { sock_release(sock); goto out; } @@ -1315,7 +1321,7 @@ static void xs_tcp_connect_worker(struct work_struct *work) } xs_reclassify_socket(sock); - if (xprt->resvport && xs_bindresvport(transport, sock) < 0) { + if (xs_bind(transport, sock)) { sock_release(sock); goto out; } @@ -1531,6 +1537,8 @@ static struct rpc_xprt *xs_setup_xprt(struct rpc_xprtsock_create *args, unsigned memcpy(&xprt->addr, args->dstaddr, args->addrlen); xprt->addrlen = args->addrlen; + if (args->srcaddr) + memcpy(&new->addr, args->srcaddr, args->addrlen); new->port = xs_get_random_port(); return xprt; -- cgit v1.1 From d8558f99fbc5ef5d4ae76b893784005056450f82 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 10 Jul 2007 15:19:26 -0400 Subject: sunrpc: drop BKL around wrap and unwrap We don't need the BKL when wrapping and unwrapping; and experiments by Avishay Traeger have found that permitting multiple encryption and decryption operations to proceed in parallel can provide significant performance improvements. Signed-off-by: J. Bruce Fields Cc: Avishay Traeger Signed-off-by: Trond Myklebust --- net/sunrpc/auth.c | 13 +++++++++++-- net/sunrpc/auth_gss/auth_gss.c | 10 ++++++++++ net/sunrpc/clnt.c | 4 ---- 3 files changed, 21 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 74baf87..aa55d0a 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -13,6 +13,7 @@ #include #include #include +#include #ifdef RPC_DEBUG # define RPCDBG_FACILITY RPCDBG_AUTH @@ -475,13 +476,17 @@ rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, __be32 *data, void *obj) { struct rpc_cred *cred = task->tk_msg.rpc_cred; + int ret; dprintk("RPC: %5u using %s cred %p to wrap rpc data\n", task->tk_pid, cred->cr_ops->cr_name, cred); if (cred->cr_ops->crwrap_req) return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj); /* By default, we encode the arguments normally. */ - return encode(rqstp, data, obj); + lock_kernel(); + ret = encode(rqstp, data, obj); + unlock_kernel(); + return ret; } int @@ -489,6 +494,7 @@ rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, __be32 *data, void *obj) { struct rpc_cred *cred = task->tk_msg.rpc_cred; + int ret; dprintk("RPC: %5u using %s cred %p to unwrap rpc data\n", task->tk_pid, cred->cr_ops->cr_name, cred); @@ -496,7 +502,10 @@ rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, return cred->cr_ops->crunwrap_resp(task, decode, rqstp, data, obj); /* By default, we decode the arguments normally. */ - return decode(rqstp, data, obj); + lock_kernel(); + ret = decode(rqstp, data, obj); + unlock_kernel(); + return ret; } int diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 17d460f..baf4096d 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -999,7 +999,9 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; *p++ = htonl(rqstp->rq_seqno); + lock_kernel(); status = encode(rqstp, p, obj); + unlock_kernel(); if (status) return status; @@ -1093,7 +1095,9 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; *p++ = htonl(rqstp->rq_seqno); + lock_kernel(); status = encode(rqstp, p, obj); + unlock_kernel(); if (status) return status; @@ -1152,12 +1156,16 @@ gss_wrap_req(struct rpc_task *task, /* The spec seems a little ambiguous here, but I think that not * wrapping context destruction requests makes the most sense. */ + lock_kernel(); status = encode(rqstp, p, obj); + unlock_kernel(); goto out; } switch (gss_cred->gc_service) { case RPC_GSS_SVC_NONE: + lock_kernel(); status = encode(rqstp, p, obj); + unlock_kernel(); break; case RPC_GSS_SVC_INTEGRITY: status = gss_wrap_req_integ(cred, ctx, encode, @@ -1273,7 +1281,9 @@ gss_unwrap_resp(struct rpc_task *task, cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + (p - savedp) + (savedlen - head->iov_len); out_decode: + lock_kernel(); status = decode(rqstp, p, obj); + unlock_kernel(); out: gss_put_ctx(ctx); dprintk("RPC: %5u gss_unwrap_resp returning %d\n", task->tk_pid, diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 5d3fe7b..52429b1 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -904,10 +904,8 @@ call_encode(struct rpc_task *task) if (encode == NULL) return; - lock_kernel(); task->tk_status = rpcauth_wrap_req(task, encode, req, p, task->tk_msg.rpc_argp); - unlock_kernel(); if (task->tk_status == -ENOMEM) { /* XXX: Is this sane? */ rpc_delay(task, 3*HZ); @@ -1238,10 +1236,8 @@ call_decode(struct rpc_task *task) task->tk_action = rpc_exit_task; if (decode) { - lock_kernel(); task->tk_status = rpcauth_unwrap_resp(task, decode, req, p, task->tk_msg.rpc_resp); - unlock_kernel(); } dprintk("RPC: %5u call_decode result %d\n", task->tk_pid, task->tk_status); -- cgit v1.1