4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 only,
8 * as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License version 2 for more details (a copy is included
14 * in the LICENSE file that accompanied this code).
16 * You should have received a copy of the GNU General Public License
17 * version 2 along with this program; If not, see
18 * http://www.gnu.org/licenses/gpl-2.0.html
23 * Range lock is used to allow multiple threads writing a single shared
24 * file given each thread is writing to a non-overlapping portion of the
27 * Refer to the possible upstream kernel version of range lock by
28 * Jan Kara <jack@suse.cz>: https://lkml.org/lkml/2013/1/31/480
30 * This file could later replaced by the upstream kernel version.
33 * Author: Prakash Surya <surya1@llnl.gov>
34 * Author: Bobi Jam <bobijam.xu@intel.com>
36 #ifdef HAVE_SCHED_HEADERS
37 #include <linux/sched/signal.h>
39 #include <linux/interval_tree_generic.h>
40 #include <uapi/linux/lustre/lustre_user.h>
41 #include <range_lock.h>
43 #define START(node) ((node)->rl_start)
44 #define LAST(node) ((node)->rl_end)
46 INTERVAL_TREE_DEFINE(struct range_lock, rl_rb, __u64, rl_subtree_last,
47 START, LAST, static, range_lock)
50 * Initialize a range lock tree
52 * \param tree [in] an empty range lock tree
54 * Pre: Caller should have allocated the range lock tree.
55 * Post: The range lock tree is ready to function.
57 void range_lock_tree_init(struct range_lock_tree *tree)
59 tree->rlt_root = INTERVAL_TREE_ROOT;
60 tree->rlt_sequence = 0;
61 spin_lock_init(&tree->rlt_lock);
63 EXPORT_SYMBOL(range_lock_tree_init);
66 * Intialize a range lock node
68 * \param lock [in] an empty range lock node
69 * \param start [in] start of the covering region
70 * \param end [in] end of the covering region
72 * Pre: Caller should have allocated the range lock node.
73 * Post: The range lock node is meant to cover [start, end] region
75 void range_lock_init(struct range_lock *lock, __u64 start, __u64 end)
78 if (end != LUSTRE_EOF)
80 lock->rl_start = start;
84 lock->rl_blocking_ranges = 0;
85 lock->rl_sequence = 0;
87 EXPORT_SYMBOL(range_lock_init);
90 * Unlock a range lock, wake up locks blocked by this lock.
92 * \param tree [in] range lock tree
93 * \param lock [in] range lock to be deleted
95 * If this lock has been granted, relase it; if not, just delete it from
96 * the tree or the same region lock list. Wake up those locks only blocked
99 void range_unlock(struct range_lock_tree *tree, struct range_lock *lock)
101 struct range_lock *overlap;
104 spin_lock(&tree->rlt_lock);
106 range_lock_remove(lock, &tree->rlt_root);
108 for (overlap = range_lock_iter_first(&tree->rlt_root,
112 overlap = range_lock_iter_next(overlap,
115 if (overlap->rl_sequence > lock->rl_sequence) {
116 --overlap->rl_blocking_ranges;
117 if (overlap->rl_blocking_ranges == 0)
118 wake_up_process(overlap->rl_task);
121 spin_unlock(&tree->rlt_lock);
125 EXPORT_SYMBOL(range_unlock);
130 * \param tree [in] range lock tree
131 * \param lock [in] range lock node containing the region span
133 * \retval 0 get the range lock
134 * \retval <0 error code while not getting the range lock
136 * If there exists overlapping range lock, the new lock will wait and
137 * retry, if later it find that it is not the chosen one to wake up,
140 int range_lock(struct range_lock_tree *tree, struct range_lock *lock)
142 struct range_lock *overlap;
146 spin_lock(&tree->rlt_lock);
148 * We need to check for all conflicting intervals
149 * already in the tree.
151 for (overlap = range_lock_iter_first(&tree->rlt_root,
155 overlap = range_lock_iter_next(overlap,
158 lock->rl_blocking_ranges += 1;
160 range_lock_insert(lock, &tree->rlt_root);
161 lock->rl_sequence = ++tree->rlt_sequence;
163 while (lock->rl_blocking_ranges > 0) {
164 lock->rl_task = current;
165 __set_current_state(TASK_INTERRUPTIBLE);
166 spin_unlock(&tree->rlt_lock);
169 if (signal_pending(current)) {
170 range_unlock(tree, lock);
171 GOTO(out, rc = -ERESTARTSYS);
173 spin_lock(&tree->rlt_lock);
175 spin_unlock(&tree->rlt_lock);
179 EXPORT_SYMBOL(range_lock);