summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEnzo Matsumiya <ematsumiya@suse.de>2025-05-08 18:01:46 -0300
committerEnzo Matsumiya <ematsumiya@suse.de>2025-05-16 17:22:57 -0300
commit67d48508a057757bb54f375b4a8f356ec3f5f8fa (patch)
tree233a5df51e24291df59a352a53fa342d3e02316c
parentc94ef99937ec2add5e524f77bf777aef21acc201 (diff)
downloadlinux-67d48508a057757bb54f375b4a8f356ec3f5f8fa.tar.gz
linux-67d48508a057757bb54f375b4a8f356ec3f5f8fa.tar.bz2
linux-67d48508a057757bb54f375b4a8f356ec3f5f8fa.zip
smb: client: spread cifsd threads through available CPUs
Spawn cifsd thread on each available CPU. Iterate in a round-robin fashion, but prefer the next one without a cifsd instance running. ~10% performance improvement is observed on heavy workloads with multichannel enabled. Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
-rw-r--r--fs/smb/client/cifsglob.h1
-rw-r--r--fs/smb/client/connect.c80
2 files changed, 66 insertions, 15 deletions
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 3b32116b0b49..d50466930c13 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -744,6 +744,7 @@ struct TCP_Server_Info {
struct mutex _srv_mutex;
unsigned int nofs_flag;
struct task_struct *tsk;
+ unsigned int tsk_cpu;
char server_GUID[16];
__u16 sec_mode;
bool sign; /* is signing enabled on this connection? */
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index 6bf04d9a5491..af728cfccf54 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -65,6 +65,17 @@ static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);
static void cifs_prune_tlinks(struct work_struct *work);
/*
+ * Stores current CPU being used by cifsd.
+ * Updated after succesfully spawning a new cifsd thread.
+ */
+static int cifsd_cur_cpu;
+/*
+ * Stores which CPUs have a cifsd thread running on it.
+ * Set/clear follows server->tsk lifetime.
+ */
+static struct cpumask cifsd_cpu_load;
+
+/*
* Resolve hostname and set ip addr in tcp ses. Useful for hostnames that may
* get their ip addresses changed at some point.
*
@@ -1123,6 +1134,7 @@ clean_demultiplex_info(struct TCP_Server_Info *server)
*/
}
+ cpumask_clear_cpu(server->tsk_cpu, &cifsd_cpu_load);
put_net(cifs_net_ns(server));
kfree(server->leaf_fullpath);
kfree(server->hostname);
@@ -1722,8 +1734,57 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
server->session_key.len = 0;
task = xchg(&server->tsk, NULL);
- if (task)
+ if (task) {
+ cpumask_clear_cpu(server->tsk_cpu, &cifsd_cpu_load);
send_sig(SIGKILL, task, 1);
+ }
+}
+
+static inline int cifsd_get_next_cpu(void)
+{
+ struct cpumask avail = {};
+ int cpu;
+
+ /* Try to get the next CPU without a cifsd instance. */
+ cpumask_andnot(&avail, cpu_online_mask, &cifsd_cpu_load);
+ if (cpumask_empty(&avail))
+ goto next_rr;
+
+ cpu = cpumask_next(cifsd_cur_cpu, &cifsd_cpu_load);
+ if (likely(cpu < nr_cpu_ids))
+ return cpu;
+next_rr:
+ /* All CPUs are running cifsd, just get the next round-robin. */
+ cpu = cpumask_next_wrap(cifsd_cur_cpu, cpu_online_mask);
+ if (likely(cpu < nr_cpu_ids))
+ return cpu;
+
+ /* Shouldn't reach as wrap above took care of returning something valid, but... */
+ return 0;
+}
+
+static int cifsd_start(struct TCP_Server_Info *server)
+{
+ int cpu, ret = 0;
+
+ /*
+ * Since we're in a cifs function already, we know that this will succeed.
+ * No need for try_module_get().
+ */
+ __module_get(THIS_MODULE);
+ cpu = cifsd_get_next_cpu();
+ server->tsk = kthread_run_on_cpu(cifs_demultiplex_thread, server, cpu, "cifsd");
+ if (!IS_ERR(server->tsk)) {
+ cpumask_set_cpu(cifsd_cur_cpu, &cifsd_cpu_load);
+ server->tsk_cpu = cifsd_cur_cpu = cpu;
+ } else {
+ ret = PTR_ERR(server->tsk);
+ server->tsk = NULL;
+ cifs_dbg(VFS, "error %d create cifsd thread\n", ret);
+ module_put(THIS_MODULE);
+ }
+
+ return ret;
}
struct TCP_Server_Info *
@@ -1851,19 +1912,10 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
goto out_err_crypto_release;
}
smbd_connected:
- /*
- * since we're in a cifs function already, we know that
- * this will succeed. No need for try_module_get().
- */
- __module_get(THIS_MODULE);
- tcp_ses->tsk = kthread_run(cifs_demultiplex_thread,
- tcp_ses, "cifsd");
- 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);
+ rc = cifsd_start(tcp_ses);
+ if (rc)
goto out_err_crypto_release;
- }
+
tcp_ses->min_offload = ctx->min_offload;
tcp_ses->retrans = ctx->retrans;
/*
@@ -1894,9 +1946,7 @@ smbd_connected:
out_err_crypto_release:
cifs_crypto_secmech_release(tcp_ses);
-
put_net(cifs_net_ns(tcp_ses));
-
out_err:
if (tcp_ses) {
if (SERVER_IS_CHAN(tcp_ses))