diff options
| author | Enzo Matsumiya <ematsumiya@suse.de> | 2025-06-18 16:54:05 -0300 |
|---|---|---|
| committer | Enzo Matsumiya <ematsumiya@suse.de> | 2025-06-18 16:54:05 -0300 |
| commit | 1195419a8176f57741d6b6a276dd37d373cbefdd (patch) | |
| tree | c327acc280eafa125a316630789de2ab5fa1e9d6 | |
| parent | b62e6fdaf55afd0dc805ac8ab1829ddba0ad3e39 (diff) | |
| download | linux-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.c | 133 |
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; } |
