162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Multipath TCP
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2022, SUSE.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#define pr_fmt(fmt) "MPTCP: " fmt
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/list.h>
1262306a36Sopenharmony_ci#include <linux/rculist.h>
1362306a36Sopenharmony_ci#include <linux/spinlock.h>
1462306a36Sopenharmony_ci#include "protocol.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(mptcp_sched_list_lock);
1762306a36Sopenharmony_cistatic LIST_HEAD(mptcp_sched_list);
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic int mptcp_sched_default_get_subflow(struct mptcp_sock *msk,
2062306a36Sopenharmony_ci					   struct mptcp_sched_data *data)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	struct sock *ssk;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	ssk = data->reinject ? mptcp_subflow_get_retrans(msk) :
2562306a36Sopenharmony_ci			       mptcp_subflow_get_send(msk);
2662306a36Sopenharmony_ci	if (!ssk)
2762306a36Sopenharmony_ci		return -EINVAL;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	mptcp_subflow_set_scheduled(mptcp_subflow_ctx(ssk), true);
3062306a36Sopenharmony_ci	return 0;
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic struct mptcp_sched_ops mptcp_sched_default = {
3462306a36Sopenharmony_ci	.get_subflow	= mptcp_sched_default_get_subflow,
3562306a36Sopenharmony_ci	.name		= "default",
3662306a36Sopenharmony_ci	.owner		= THIS_MODULE,
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* Must be called with rcu read lock held */
4062306a36Sopenharmony_cistruct mptcp_sched_ops *mptcp_sched_find(const char *name)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct mptcp_sched_ops *sched, *ret = NULL;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	list_for_each_entry_rcu(sched, &mptcp_sched_list, list) {
4562306a36Sopenharmony_ci		if (!strcmp(sched->name, name)) {
4662306a36Sopenharmony_ci			ret = sched;
4762306a36Sopenharmony_ci			break;
4862306a36Sopenharmony_ci		}
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	return ret;
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ciint mptcp_register_scheduler(struct mptcp_sched_ops *sched)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	if (!sched->get_subflow)
5762306a36Sopenharmony_ci		return -EINVAL;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	spin_lock(&mptcp_sched_list_lock);
6062306a36Sopenharmony_ci	if (mptcp_sched_find(sched->name)) {
6162306a36Sopenharmony_ci		spin_unlock(&mptcp_sched_list_lock);
6262306a36Sopenharmony_ci		return -EEXIST;
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci	list_add_tail_rcu(&sched->list, &mptcp_sched_list);
6562306a36Sopenharmony_ci	spin_unlock(&mptcp_sched_list_lock);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	pr_debug("%s registered", sched->name);
6862306a36Sopenharmony_ci	return 0;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_civoid mptcp_unregister_scheduler(struct mptcp_sched_ops *sched)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	if (sched == &mptcp_sched_default)
7462306a36Sopenharmony_ci		return;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	spin_lock(&mptcp_sched_list_lock);
7762306a36Sopenharmony_ci	list_del_rcu(&sched->list);
7862306a36Sopenharmony_ci	spin_unlock(&mptcp_sched_list_lock);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_civoid mptcp_sched_init(void)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	mptcp_register_scheduler(&mptcp_sched_default);
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciint mptcp_init_sched(struct mptcp_sock *msk,
8762306a36Sopenharmony_ci		     struct mptcp_sched_ops *sched)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	if (!sched)
9062306a36Sopenharmony_ci		sched = &mptcp_sched_default;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (!bpf_try_module_get(sched, sched->owner))
9362306a36Sopenharmony_ci		return -EBUSY;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	msk->sched = sched;
9662306a36Sopenharmony_ci	if (msk->sched->init)
9762306a36Sopenharmony_ci		msk->sched->init(msk);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	pr_debug("sched=%s", msk->sched->name);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return 0;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_civoid mptcp_release_sched(struct mptcp_sock *msk)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct mptcp_sched_ops *sched = msk->sched;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (!sched)
10962306a36Sopenharmony_ci		return;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	msk->sched = NULL;
11262306a36Sopenharmony_ci	if (sched->release)
11362306a36Sopenharmony_ci		sched->release(msk);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	bpf_module_put(sched, sched->owner);
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_civoid mptcp_subflow_set_scheduled(struct mptcp_subflow_context *subflow,
11962306a36Sopenharmony_ci				 bool scheduled)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	WRITE_ONCE(subflow->scheduled, scheduled);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ciint mptcp_sched_get_send(struct mptcp_sock *msk)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct mptcp_subflow_context *subflow;
12762306a36Sopenharmony_ci	struct mptcp_sched_data data;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	msk_owned_by_me(msk);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* the following check is moved out of mptcp_subflow_get_send */
13262306a36Sopenharmony_ci	if (__mptcp_check_fallback(msk)) {
13362306a36Sopenharmony_ci		if (msk->first &&
13462306a36Sopenharmony_ci		    __tcp_can_send(msk->first) &&
13562306a36Sopenharmony_ci		    sk_stream_memory_free(msk->first)) {
13662306a36Sopenharmony_ci			mptcp_subflow_set_scheduled(mptcp_subflow_ctx(msk->first), true);
13762306a36Sopenharmony_ci			return 0;
13862306a36Sopenharmony_ci		}
13962306a36Sopenharmony_ci		return -EINVAL;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	mptcp_for_each_subflow(msk, subflow) {
14362306a36Sopenharmony_ci		if (READ_ONCE(subflow->scheduled))
14462306a36Sopenharmony_ci			return 0;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	data.reinject = false;
14862306a36Sopenharmony_ci	if (msk->sched == &mptcp_sched_default || !msk->sched)
14962306a36Sopenharmony_ci		return mptcp_sched_default_get_subflow(msk, &data);
15062306a36Sopenharmony_ci	return msk->sched->get_subflow(msk, &data);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ciint mptcp_sched_get_retrans(struct mptcp_sock *msk)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct mptcp_subflow_context *subflow;
15662306a36Sopenharmony_ci	struct mptcp_sched_data data;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	msk_owned_by_me(msk);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* the following check is moved out of mptcp_subflow_get_retrans */
16162306a36Sopenharmony_ci	if (__mptcp_check_fallback(msk))
16262306a36Sopenharmony_ci		return -EINVAL;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	mptcp_for_each_subflow(msk, subflow) {
16562306a36Sopenharmony_ci		if (READ_ONCE(subflow->scheduled))
16662306a36Sopenharmony_ci			return 0;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	data.reinject = true;
17062306a36Sopenharmony_ci	if (msk->sched == &mptcp_sched_default || !msk->sched)
17162306a36Sopenharmony_ci		return mptcp_sched_default_get_subflow(msk, &data);
17262306a36Sopenharmony_ci	return msk->sched->get_subflow(msk, &data);
17362306a36Sopenharmony_ci}
174