162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Common code for control of lockd and nfsv4 grace periods.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Transplanted from lockd code
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <net/net_namespace.h>
1062306a36Sopenharmony_ci#include <net/netns/generic.h>
1162306a36Sopenharmony_ci#include <linux/fs.h>
1262306a36Sopenharmony_ci#include <linux/filelock.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic unsigned int grace_net_id;
1562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(grace_lock);
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/**
1862306a36Sopenharmony_ci * locks_start_grace
1962306a36Sopenharmony_ci * @net: net namespace that this lock manager belongs to
2062306a36Sopenharmony_ci * @lm: who this grace period is for
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * A grace period is a period during which locks should not be given
2362306a36Sopenharmony_ci * out.  Currently grace periods are only enforced by the two lock
2462306a36Sopenharmony_ci * managers (lockd and nfsd), using the locks_in_grace() function to
2562306a36Sopenharmony_ci * check when they are in a grace period.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * This function is called to start a grace period.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_civoid
3062306a36Sopenharmony_cilocks_start_grace(struct net *net, struct lock_manager *lm)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct list_head *grace_list = net_generic(net, grace_net_id);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	spin_lock(&grace_lock);
3562306a36Sopenharmony_ci	if (list_empty(&lm->list))
3662306a36Sopenharmony_ci		list_add(&lm->list, grace_list);
3762306a36Sopenharmony_ci	else
3862306a36Sopenharmony_ci		WARN(1, "double list_add attempt detected in net %x %s\n",
3962306a36Sopenharmony_ci		     net->ns.inum, (net == &init_net) ? "(init_net)" : "");
4062306a36Sopenharmony_ci	spin_unlock(&grace_lock);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(locks_start_grace);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/**
4562306a36Sopenharmony_ci * locks_end_grace
4662306a36Sopenharmony_ci * @lm: who this grace period is for
4762306a36Sopenharmony_ci *
4862306a36Sopenharmony_ci * Call this function to state that the given lock manager is ready to
4962306a36Sopenharmony_ci * resume regular locking.  The grace period will not end until all lock
5062306a36Sopenharmony_ci * managers that called locks_start_grace() also call locks_end_grace().
5162306a36Sopenharmony_ci * Note that callers count on it being safe to call this more than once,
5262306a36Sopenharmony_ci * and the second call should be a no-op.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_civoid
5562306a36Sopenharmony_cilocks_end_grace(struct lock_manager *lm)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	spin_lock(&grace_lock);
5862306a36Sopenharmony_ci	list_del_init(&lm->list);
5962306a36Sopenharmony_ci	spin_unlock(&grace_lock);
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(locks_end_grace);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic bool
6462306a36Sopenharmony_ci__state_in_grace(struct net *net, bool open)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct list_head *grace_list = net_generic(net, grace_net_id);
6762306a36Sopenharmony_ci	struct lock_manager *lm;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if (!open)
7062306a36Sopenharmony_ci		return !list_empty(grace_list);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	spin_lock(&grace_lock);
7362306a36Sopenharmony_ci	list_for_each_entry(lm, grace_list, list) {
7462306a36Sopenharmony_ci		if (lm->block_opens) {
7562306a36Sopenharmony_ci			spin_unlock(&grace_lock);
7662306a36Sopenharmony_ci			return true;
7762306a36Sopenharmony_ci		}
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci	spin_unlock(&grace_lock);
8062306a36Sopenharmony_ci	return false;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/**
8462306a36Sopenharmony_ci * locks_in_grace
8562306a36Sopenharmony_ci * @net: network namespace
8662306a36Sopenharmony_ci *
8762306a36Sopenharmony_ci * Lock managers call this function to determine when it is OK for them
8862306a36Sopenharmony_ci * to answer ordinary lock requests, and when they should accept only
8962306a36Sopenharmony_ci * lock reclaims.
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_cibool locks_in_grace(struct net *net)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	return __state_in_grace(net, false);
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(locks_in_grace);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cibool opens_in_grace(struct net *net)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	return __state_in_grace(net, true);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opens_in_grace);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic int __net_init
10462306a36Sopenharmony_cigrace_init_net(struct net *net)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct list_head *grace_list = net_generic(net, grace_net_id);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	INIT_LIST_HEAD(grace_list);
10962306a36Sopenharmony_ci	return 0;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic void __net_exit
11362306a36Sopenharmony_cigrace_exit_net(struct net *net)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct list_head *grace_list = net_generic(net, grace_net_id);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	WARN_ONCE(!list_empty(grace_list),
11862306a36Sopenharmony_ci		  "net %x %s: grace_list is not empty\n",
11962306a36Sopenharmony_ci		  net->ns.inum, __func__);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic struct pernet_operations grace_net_ops = {
12362306a36Sopenharmony_ci	.init = grace_init_net,
12462306a36Sopenharmony_ci	.exit = grace_exit_net,
12562306a36Sopenharmony_ci	.id   = &grace_net_id,
12662306a36Sopenharmony_ci	.size = sizeof(struct list_head),
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic int __init
13062306a36Sopenharmony_ciinit_grace(void)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	return register_pernet_subsys(&grace_net_ops);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic void __exit
13662306a36Sopenharmony_ciexit_grace(void)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	unregister_pernet_subsys(&grace_net_ops);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ciMODULE_AUTHOR("Jeff Layton <jlayton@primarydata.com>");
14262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
14362306a36Sopenharmony_cimodule_init(init_grace)
14462306a36Sopenharmony_cimodule_exit(exit_grace)
145