diff options
Diffstat (limited to 'fs/smb/client/connect.c')
| -rw-r--r-- | fs/smb/client/connect.c | 54 |
1 files changed, 52 insertions, 2 deletions
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index 8e9a672320ab..c808e2662857 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -8,6 +8,7 @@ #include <linux/fs.h> #include <linux/net.h> #include <linux/string.h> +#include <linux/sched.h> #include <linux/sched/mm.h> #include <linux/sched/signal.h> #include <linux/list.h> @@ -30,6 +31,7 @@ #include <linux/module.h> #include <keys/user-type.h> #include <net/ipv6.h> +#include <net/route.h> #include <linux/parser.h> #include <linux/bvec.h> #include "cifspdu.h" @@ -67,6 +69,8 @@ static int generic_ip_connect(struct TCP_Server_Info *server); static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink); static void cifs_prune_tlinks(struct work_struct *work); +static int cifsd_rr_last_cpu = -1; + /* * Resolve hostname and set ip addr in tcp ses. Useful for hostnames that may * get their ip addresses changed at some point. @@ -1550,6 +1554,45 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect) send_sig(SIGKILL, task, 1); } +/** + * __smb_get_next_cpu: return the next online CPU, globally accounted by @cifsd_rr_last_cpu + */ +static inline int __smb_get_next_cpu(void) +{ + int cpu; + + cpu = cpumask_next_wrap(cifsd_rr_last_cpu, cpu_online_mask, -1, false); + if (unlikely(cpu >= nr_cpu_ids)) { + cpu = cpumask_next(raw_smp_processor_id(), cpu_online_mask); + if (unlikely(cpu >= nr_cpu_ids)) { + /* last resort */ + cpu = raw_smp_processor_id(); + } + } + + cifsd_rr_last_cpu = cpu; + return cifsd_rr_last_cpu; +} + +/** + * smb_get_cifsd_task: return a task_struct for cifsd, based on @server::cpu_policy_affine + * + * @server::cpu_policy_affine behaviour: + * true: create the task via kthread_run() which will let the scheduler pick the best CPU to use + * false: create the task via kthread_run_on_cpu(), with its @cpu argument set to the next + * online CPU, which is chosen in a round-robin manner in __smb_get_next_cpu() and tracked + * via global %cifsd_rr_last_cpu + */ +static inline struct task_struct *smb_get_cifsd_task(struct TCP_Server_Info *server) +{ + if (!server->cpu_policy_affine || num_online_cpus() == 1) + return kthread_run(cifs_demultiplex_thread, server, "cifsd"); + + /* cpu_policy=affine */ + return kthread_run_on_cpu(cifs_demultiplex_thread, server, __smb_get_next_cpu(), + "cifsd"); +} + struct TCP_Server_Info * cifs_get_tcp_session(struct smb3_fs_context *ctx, struct TCP_Server_Info *primary_server) @@ -1680,14 +1723,21 @@ smbd_connected: * this will succeed. No need for try_module_get(). */ __module_get(THIS_MODULE); - tcp_ses->tsk = kthread_run(cifs_demultiplex_thread, - tcp_ses, "cifsd"); + + tcp_ses->cpu_policy_affine = tcp_ses->primary_server ? + tcp_ses->primary_server->cpu_policy_affine : + ctx->cpu_policy_affine; + tcp_ses->tsk = smb_get_cifsd_task(tcp_ses); if (IS_ERR(tcp_ses->tsk)) { rc = PTR_ERR(tcp_ses->tsk); cifs_dbg(VFS, "error %d create cifsd thread\n", rc); module_put(THIS_MODULE); goto out_err_crypto_release; } + + cifs_dbg(FYI, "%s: running cifsd task 0x%p on CPU#%d\n", + __func__, tcp_ses->tsk, tcp_ses->tsk_cpu); + tcp_ses->min_offload = ctx->min_offload; /* * at this point we are the only ones with the pointer |
