summaryrefslogtreecommitdiff
path: root/fs/smb/client/connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/smb/client/connect.c')
-rw-r--r--fs/smb/client/connect.c54
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