summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEnzo Matsumiya <ematsumiya@suse.de>2025-06-22 17:04:10 -0300
committerEnzo Matsumiya <ematsumiya@suse.de>2025-06-22 17:04:10 -0300
commit5aea01db0af37260e4a0a0d17b6f22f4f1764d7e (patch)
tree62b2060c58bfd79ef6808103314933be93468f85
parentd829bbabf99c4bfbdb82a1a8d24d71e76f3dd53b (diff)
downloadlinux-multichannel-fixes-v3.tar.gz
linux-multichannel-fixes-v3.tar.bz2
linux-multichannel-fixes-v3.zip
smb: client: multichannel picking WIPmultichannel-fixes-v3
Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
-rw-r--r--fs/smb/client/cifsglob.h7
-rw-r--r--fs/smb/client/cifsproto.h1
-rw-r--r--fs/smb/client/connect.c7
-rw-r--r--fs/smb/client/file.c4
-rw-r--r--fs/smb/client/misc.c3
-rw-r--r--fs/smb/client/sess.c5
-rw-r--r--fs/smb/client/smb2pdu.c6
-rw-r--r--fs/smb/client/transport.c134
8 files changed, 62 insertions, 105 deletions
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 24d05d16d70e..436d96484ec1 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -1101,6 +1101,8 @@ release_iface(struct kref *ref)
struct cifs_chan {
struct list_head head;
+ size_t used;
+ size_t same_cpu;
unsigned int in_reconnect : 1; /* if session setup in progress for this channel */
struct TCP_Server_Info *server;
struct cifs_server_iface *iface; /* interface in use */
@@ -1172,11 +1174,12 @@ struct cifs_ses {
((ses)->chans[(index)].in_reconnect)
struct cifs_chan chans[CIFS_MAX_CHANNELS]; /* this only stores channels for management */
- struct list_head chans_lru; /* for cifs_pick_channel() */
- struct list_head chans_lru_io; /* for cifs_pick_io_channel() */
+ struct list_head chans_prio; /* for cifs_pick_channel() */
size_t chan_count;
size_t chan_max;
+
size_t fast_picks;
+ size_t fast_first;
size_t slow_picks;
size_t chan_changes;
diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
index 3fd18ef176ef..ea11f718a69d 100644
--- a/fs/smb/client/cifsproto.h
+++ b/fs/smb/client/cifsproto.h
@@ -101,7 +101,6 @@ extern int cifs_call_async(struct TCP_Server_Info *server,
mid_handle_t *handle, void *cbdata, const int flags,
const struct cifs_credits *exist_credits);
extern struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses);
-extern struct TCP_Server_Info *cifs_pick_io_channel(struct cifs_ses *ses);
extern int cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
struct TCP_Server_Info *server,
struct smb_rqst *rqst, int *resp_buf_type,
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index 90c8286ae39d..104e44092ceb 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -1751,6 +1751,7 @@ static int cifsd_start(struct TCP_Server_Info *server)
if (!IS_ERR(server->tsk)) {
cpumask_set_cpu(cifsd_cur_cpu, &cifsd_cpu_load);
server->tsk_cpu = cifsd_cur_cpu = cpu;
+ pr_err("%s: starting on CPU %d\n", __func__, cpu);
} else {
ret = PTR_ERR(server->tsk);
server->tsk = NULL;
@@ -2180,10 +2181,12 @@ void __cifs_put_smb_ses(struct cifs_ses *ses)
list_del_init(&ses->smb_ses_list);
spin_unlock(&cifs_tcp_ses_lock);
- pr_err("%s: channel changes %zu, fast %zu, slow %zu\n", __func__, ses->chan_changes, ses->fast_picks, ses->slow_picks);
+ pr_err("%s: channel changes %zu, fast %zu (first picks %zu), slow %zu\n", __func__, ses->chan_changes, ses->fast_picks, ses->fast_first, ses->slow_picks);
/* close any extra channels */
+ pr_err("%s: chan 0 used %zu times (recent cpu %zu)\n", __func__, ses->chans[0].used, ses->chans[0].same_cpu);
for (i = 1; i < ses->chan_count; i++) {
+ pr_err("%s: chan %zu used %zu times (recent cpu %zu)\n", __func__, i, ses->chans[i].used, ses->chans[i].same_cpu);
list_del_init(&ses->chans[i].head);
if (ses->chans[i].iface) {
kref_put(&ses->chans[i].iface->refcount, release_iface);
@@ -2512,7 +2515,7 @@ retry_old_session:
/* add server as first channel */
spin_lock(&ses->chan_lock);
ses->chans[0].server = server;
- list_add(&ses->chans[0].head, &ses->chans_lru);
+ list_add(&ses->chans[0].head, &ses->chans_prio);
ses->chan_count = 1;
ses->chan_max = ctx->multichannel ? ctx->max_channels:1;
ses->chans_need_reconnect = 1;
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index aae2d5ff5176..9835672267d2 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -60,7 +60,7 @@ static void cifs_prepare_write(struct netfs_io_subrequest *subreq)
wdata->have_xid = true;
}
- server = cifs_pick_io_channel(tlink_tcon(open_file->tlink)->ses);
+ server = cifs_pick_channel(tlink_tcon(open_file->tlink)->ses);
wdata->server = server;
retry:
@@ -157,7 +157,7 @@ static int cifs_prepare_read(struct netfs_io_subrequest *subreq)
rdata->have_xid = true;
}
- server = cifs_pick_io_channel(tlink_tcon(req->cfile->tlink)->ses);
+ server = cifs_pick_channel(tlink_tcon(req->cfile->tlink)->ses);
rdata->server = server;
if (cifs_sb->ctx->rsize == 0) {
diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
index a0962aeef79f..43fd3b95ec91 100644
--- a/fs/smb/client/misc.c
+++ b/fs/smb/client/misc.c
@@ -74,8 +74,7 @@ sesInfoAlloc(void)
++ret_buf->ses_count;
INIT_LIST_HEAD(&ret_buf->smb_ses_list);
INIT_LIST_HEAD(&ret_buf->tcon_list);
- INIT_LIST_HEAD(&ret_buf->chans_lru);
- INIT_LIST_HEAD(&ret_buf->chans_lru_io);
+ INIT_LIST_HEAD(&ret_buf->chans_prio);
mutex_init(&ret_buf->session_mutex);
spin_lock_init(&ret_buf->chan_lock);
}
diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c
index 68e405bfe9cc..b26ede68e889 100644
--- a/fs/smb/client/sess.c
+++ b/fs/smb/client/sess.c
@@ -565,10 +565,9 @@ cifs_ses_add_channel(struct cifs_ses *ses,
rc = cifs_negotiate_protocol(xid, ses, chan->server);
if (!rc) {
rc = cifs_setup_session(xid, ses, chan->server, ses->local_nls);
- if (!rc) {
+ if (!rc)
/* channel is ready to be used */
- list_add(&chan->head, &ses->chans_lru);
- }
+ list_add_tail(&chan->head, &ses->chans_prio);
}
mutex_unlock(&ses->session_mutex);
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 776e1d3d4c2f..6a31c0de0e69 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -3387,11 +3387,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
replay_again:
/* reinitialize for possible replay */
flags = 0;
- if (opcode == FSCTL_SRV_COPYCHUNK || opcode == FSCTL_SRV_COPYCHUNK_WRITE ||
- opcode == FSCTL_SET_ZERO_DATA)
- server = cifs_pick_io_channel(ses);
- else
- server = cifs_pick_channel(ses);
+ server = cifs_pick_channel(ses);
if (!server)
return -EIO;
diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c
index f25f075293c3..00560120e0ca 100644
--- a/fs/smb/client/transport.c
+++ b/fs/smb/client/transport.c
@@ -1006,101 +1006,76 @@ cifs_cancelled_callback(struct mid_q_entry *mid)
release_mid(mid);
}
-enum {
- CIFS_CHAN_GOOD,
- CIFS_CHAN_SLOW,
- CIFS_CHAN_STUCK,
-};
+static inline bool chan_is_stuck(struct TCP_Server_Info *chan)
+{
+ unsigned long echo = chan->echo_interval;
+
+ if (echo)
+ echo = msecs_to_jiffies(echo * 1000);
+ else
+ echo = msecs_to_jiffies(SMB_ECHO_INTERVAL_DEFAULT * 3 * 1000);
-static inline int chan_responsiveness(struct cifs_chan *chan, unsigned int in_flight)
+ return time_is_before_jiffies(chan->lstrp + echo);
+}
+
+static inline bool chan_is_good(struct TCP_Server_Info *chan)
{
- unsigned long lstrp = chan->server->lstrp;
- unsigned long echo = msecs_to_jiffies(chan->server->echo_interval * 1000);
- unsigned long now = jiffies;
unsigned long threshold;
- if (in_flight == 0)
- return CIFS_CHAN_GOOD;
-
threshold = msecs_to_jiffies(clamp_t(unsigned long, slow_rsp_threshold * 1000, 500, 3000));
- if (time_after(lstrp + threshold, now))
- return CIFS_CHAN_GOOD;
- if (time_after(lstrp + echo, now))
- return CIFS_CHAN_SLOW;
+ if (chan->in_flight < 10)
+ return time_is_after_jiffies(chan->lstrp + threshold);
- return CIFS_CHAN_STUCK;
+ return !chan_is_stuck(chan);
}
-static struct cifs_chan *fast_pick_channel(struct list_head *list)
+static struct cifs_chan *fast_pick_channel(struct cifs_ses *ses)
{
struct cifs_chan *cur, *next = NULL;
- if (likely(!list_is_singular(list))) {
- cur = list_first_entry(list, struct cifs_chan, head);
-check_next:
- if (cur->server->in_flight < 5) {
- if (chan_responsiveness(cur, cur->server->in_flight) == CIFS_CHAN_GOOD)
- return cur;
-
- if (!next) {
- next = list_next_entry_circular(cur, list, head);
- cur = next;
- goto check_next;
- }
- }
+ cur = list_first_entry(&ses->chans_prio, struct cifs_chan, head);
+ if (chan_is_good(cur->server)) {
+ ses->fast_first++;
+ return cur;
}
- return NULL;
-}
+ if (likely(!list_is_singular(&ses->chans_prio))) {
+ next = list_next_entry_circular(cur, &ses->chans_prio, head);
+ if (chan_is_good(next->server))
+ return next;
+ }
-static struct cifs_chan *slow_pick_channel(struct cifs_ses *ses)
-{
- int i;
+ // warm path
+ if (!chan_is_stuck(cur->server))
+ return cur;
- for (i = 1; i < ses->chan_count; i++)
- if (chan_responsiveness(&ses->chans[i], ses->chans[i].server->in_flight) == CIFS_CHAN_GOOD)
- return &ses->chans[i];
+ if (next && !chan_is_stuck(next->server))
+ return next;
- return &ses->chans[0];
+ // cold, will call slow pick
+ return NULL;
}
-#if 0
-static struct TCP_Server_Info *pick_channel(struct cifs_ses *ses, bool io)
+static struct cifs_chan *slow_pick_channel(struct cifs_ses *ses)
{
struct cifs_chan *chan;
+ int i;
- spin_lock(&ses->chan_lock);
- if (ses->chan_count == 1) {
- chan = &ses->chans[0];
- goto out;
- }
+ for (i = 0; i < ses->chan_count; i++) {
+ chan = &ses->chans[i];
- chan = fast_pick_channel(&ses->chans_lru);
- if (likely(chan)) {
- ses->fast_picks++;
- goto out;
- }
+ if (chan_is_good(chan->server) || !chan_is_stuck(ses->chans[0].server))
+ return chan;
- chan = fast_pick_channel(&ses->chans_lru_io);
- if (chan) {
- ses->fast_picks++;
- goto out;
+ cifs_dbg(VFS, "%s: channel %d might be stuck (last response %lus ago)\n",
+ __func__, i, (jiffies - chan->server->lstrp) / HZ);
}
- ses->slow_picks++;
- chan = slow_pick_channel(ses);
-out:
- if (!io)
- list_move_tail(&chan->head, &ses->chans_lru);
- else
- list_move_tail(&chan->head, &ses->chans_lru_io);
- spin_unlock(&ses->chan_lock);
- return chan->server;
+ return &ses->chans[0];
}
-#endif
-static struct TCP_Server_Info *pick_channel(struct cifs_ses *ses, bool io)
+struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses)
{
struct cifs_chan *chan;
@@ -1110,39 +1085,22 @@ static struct TCP_Server_Info *pick_channel(struct cifs_ses *ses, bool io)
goto out;
}
- chan = fast_pick_channel(&ses->chans_lru);
+ chan = fast_pick_channel(ses);
if (likely(chan)) {
ses->fast_picks++;
goto out;
}
- chan = fast_pick_channel(&ses->chans_lru_io);
- if (chan) {
- ses->fast_picks++;
- goto out;
- }
-
ses->slow_picks++;
chan = slow_pick_channel(ses);
out:
- if (!io)
- list_move_tail(&chan->head, &ses->chans_lru);
- else
- list_move_tail(&chan->head, &ses->chans_lru_io);
+ list_move_tail(&chan->head, &ses->chans_prio);
+ chan->used++;
+
spin_unlock(&ses->chan_lock);
return chan->server;
}
-struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses)
-{
- return pick_channel(ses, false);
-}
-
-struct TCP_Server_Info *cifs_pick_io_channel(struct cifs_ses *ses)
-{
- return pick_channel(ses, true);
-}
-
int
compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
struct TCP_Server_Info *server,