summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEnzo Matsumiya <ematsumiya@suse.de>2025-06-18 16:54:05 -0300
committerEnzo Matsumiya <ematsumiya@suse.de>2025-06-18 16:54:05 -0300
commit1195419a8176f57741d6b6a276dd37d373cbefdd (patch)
treec327acc280eafa125a316630789de2ab5fa1e9d6
parentb62e6fdaf55afd0dc805ac8ab1829ddba0ad3e39 (diff)
downloadlinux-1195419a8176f57741d6b6a276dd37d373cbefdd.tar.gz
linux-1195419a8176f57741d6b6a276dd37d373cbefdd.tar.bz2
linux-1195419a8176f57741d6b6a276dd37d373cbefdd.zip
smb: client: chan selection based on latency/throughput
Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
-rw-r--r--fs/smb/client/transport.c133
1 files changed, 74 insertions, 59 deletions
diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c
index 880471573251..687d410cc5b1 100644
--- a/fs/smb/client/transport.c
+++ b/fs/smb/client/transport.c
@@ -1011,88 +1011,106 @@ cifs_cancelled_callback(struct mid_q_entry *mid)
static inline bool is_chan_usable(struct cifs_chan *chan)
{
+#if 0
return (chan->server && !chan->server->terminate && !chan->in_reconnect);
+#endif
+ return true;
+}
+
+#define CHAN_THROUGHPUT_THRESHOLD ((u64)1 << 32) /* 1 in_flight unit */
+#define CHAN_LATENCY_THRESHOLD ((u64)1000) /* 1 millisecond (in SRTT units) */
+
+static u64 chan_latency_score(struct TCP_Server_Info *s)
+{
+ u32 lstrq = atomic_long_read(&s->lstrq);
+ u32 lstrp = atomic_long_read(&s->lstrp);
+ u32 diff = (lstrq > lstrp ? lstrq - lstrp : lstrp - lstrq);
+ u32 srtt = tcp_sk(s->ssocket->sk)->srtt_us >> 3;
+
+ return ((u64)diff << 32) | (u64)srtt;
+}
+
+static u64 chan_throughput_threshold(struct cifs_ses *ses)
+{
+ u64 scaled = CHAN_THROUGHPUT_THRESHOLD * ses->chan_count;
+ u64 credits = 0;
+ int i;
+
+ for (i = 0; i < ses->chan_count; i++)
+ credits += ses->chans[i].server->credits;
+
+ credits = credits / ses->chan_count;
+ credits = ((U32_MAX - credits) >> 4) << 16;
+
+ return scaled > credits ? scaled - credits : 0;
+}
+
+static u64 chan_throughput_score(struct TCP_Server_Info *s)
+{
+ u32 inflight = s->in_flight;
+ u32 cred_cost = U32_MAX - s->credits;
+
+ return ((u64)inflight << 32) | ((u64)cred_cost << 16);
}
/*
- * Choose @a or @b based on requests in flight, lstrq and lstrp, and SRTT.
+ * Choose @a or @b based on lstrq and lstrp, and SRTT.
*
* Algorithm is made to prefer @a for equal or default cases, so callers should adjust their
* arguments to that.
*
* Return: 0 if @a is preferred, 1 if @b
*/
-static inline int chan_cmp_load(struct TCP_Server_Info *a, struct TCP_Server_Info *b)
+static inline int chan_cmp_latency(struct TCP_Server_Info *a, struct TCP_Server_Info *b, u64 thresh)
{
- //unsigned int a_srtt, b_srtt;
- unsigned long lstrq, lstrp, a_delta, b_delta;
- const unsigned long max_delta = 200;
-
- lstrq = atomic_long_read(&a->lstrq);
- lstrp = atomic_long_read(&a->lstrp);
- a_delta = ((lstrq > lstrp) ? lstrq - lstrp : lstrp - lstrq);
- if (a_delta < max_delta)
- return 0;
+ u64 a_score = chan_latency_score(a);
+ u64 b_score = chan_latency_score(b);
- lstrq = atomic_long_read(&b->lstrq);
- lstrp = atomic_long_read(&b->lstrp);
- b_delta = ((lstrq > lstrp) ? lstrq - lstrp : lstrp - lstrq);
- if (b_delta < max_delta)
- return 1;
+ return (a_score - thresh > b_score);
+}
- /*
- * Neither @a or @b are responding as fast as we wanted, check requests in flight.
- *
- * Strictly speaking, we should pick up req_lock to read server->in_flight. But it
- * shouldn't matter much here if we race while reading this data. The worst that can
- * happen is that we could use a channel that's not least loaded. Avoiding taking the
- * lock could help reduce wait time, which is important for this function.
- */
- if (a->in_flight <= b->in_flight) {
- if (a_delta <= b_delta)
- return 0;
- return 1;
- } else if (a_delta <= b_delta) {
- return 0;
- }
+/*
+ * Choose @a or @b based on requests in flight and credits available.
+ *
+ * Algorithm is made to prefer @a for equal or default cases, so callers should adjust their
+ * arguments to that.
+ *
+ * Return: 0 if @a is preferred, 1 if @b
+ */
+static inline int chan_cmp_load(struct TCP_Server_Info *a, struct TCP_Server_Info *b,
+ u64 lat_thresh, u64 io_thresh)
+{
+ u64 a_score = chan_throughput_score(a);
+ u64 b_score = chan_throughput_score(b);
- return 1;
-#if 0
- /* Pick the fastest responding (smoothed RTT) socket */
- a_srtt = tcp_sk(a->ssocket->sk)->srtt_us >> 3;
- b_srtt = tcp_sk(b->ssocket->sk)->srtt_us >> 3;
+ if (a_score + io_thresh < b_score)
+ return 0;
- return (a_srtt > b_srtt);
-#endif
+ return chan_cmp_latency(a, b, lat_thresh);
}
-static struct cifs_chan *slow_pick_channel(struct cifs_ses *ses)
+static struct cifs_chan *slow_pick_channel(struct cifs_ses *ses, u64 lat_thresh, u64 io_thresh)
{
struct cifs_chan *cur, *best = &ses->chans[0];
int i;
- cond_resched();
-
- spin_lock(&ses->chan_lock);
for (i = 1; i < ses->chan_count; i++) {
cur = &ses->chans[i];
if (is_chan_usable(cur)) {
- if (!chan_cmp_load(cur->server, best->server))
+ if (!chan_cmp_load(cur->server, best->server, lat_thresh, io_thresh))
best = cur;
}
}
list_move_tail(&best->lru_head, &ses->chans_lru);
- spin_unlock(&ses->chan_lock);
return best;
}
-static struct cifs_chan *fast_pick_channel(struct cifs_ses *ses)
+static struct cifs_chan *fast_pick_channel(struct cifs_ses *ses, u64 lat_thresh, u64 io_thresh)
{
struct cifs_chan *cur, *next;
- spin_lock(&ses->chan_lock);
if (ses->chan_count == 1) {
cur = &ses->chans[0];
goto out;
@@ -1102,7 +1120,7 @@ static struct cifs_chan *fast_pick_channel(struct cifs_ses *ses)
if (likely(!list_is_singular(&ses->chans_lru))) {
next = list_next_entry(cur, lru_head);
if (likely(is_chan_usable(cur) && is_chan_usable(next))) {
- if (!chan_cmp_load(next->server, cur->server))
+ if (!chan_cmp_load(next->server, cur->server, lat_thresh, io_thresh))
cur = next;
goto out;
@@ -1114,36 +1132,33 @@ out:
/* bump chan to 'most recently used' */
list_move_tail(&cur->lru_head, &ses->chans_lru);
- spin_unlock(&ses->chan_lock);
-
return cur;
}
/*
- * Return a channel (master if none) of @ses that can be used to send
+ * Return a channel (master if none) of @ses, based on best latency, that can be used to send
* regular requests.
- *
- * @ses->chans_lru head is the least recent used channel.
- * Check its load, compare it to the next least recent used channel.
- *
- * Return the most suitable channel between both.
- *
- * Move the selected channel to the end (most recently used) of @ses->chans_lru.
*/
struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses)
{
struct cifs_chan *chan;
+ u64 lat_thresh, io_thresh;
if (likely(ses && ses->server)) {
- chan = fast_pick_channel(ses);
+ spin_lock(&ses->chan_lock);
+ lat_thresh = CHAN_LATENCY_THRESHOLD * ses->chan_count;
+ io_thresh = chan_throughput_threshold(ses);
+ chan = fast_pick_channel(ses, lat_thresh, io_thresh);
if (likely(chan)) {
ses->fast_picks++;
+ spin_unlock(&ses->chan_lock);
return chan->server;
}
/* slow_pick_channel() never returns NULL, no need to check */
- chan = slow_pick_channel(ses);
+ chan = slow_pick_channel(ses, lat_thresh, io_thresh);
ses->slow_picks++;
+ spin_unlock(&ses->chan_lock);
return chan->server;
}