Blame view

fs/btrfs/locking.c 5.74 KB
925baeddc   Chris Mason   Btrfs: Start btre...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  /*
   * Copyright (C) 2008 Oracle.  All rights reserved.
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public
   * License v2 as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * General Public License for more details.
   *
   * You should have received a copy of the GNU General Public
   * License along with this program; if not, write to the
   * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   * Boston, MA 021110-1307, USA.
   */
  #include <linux/sched.h>
925baeddc   Chris Mason   Btrfs: Start btre...
19
20
21
  #include <linux/pagemap.h>
  #include <linux/spinlock.h>
  #include <linux/page-flags.h>
4881ee5a2   Chris Mason   Btrfs: Fix some b...
22
  #include <asm/bug.h>
925baeddc   Chris Mason   Btrfs: Start btre...
23
24
25
  #include "ctree.h"
  #include "extent_io.h"
  #include "locking.h"
bd681513f   Chris Mason   Btrfs: switch the...
26
  void btrfs_assert_tree_read_locked(struct extent_buffer *eb);
d397712bc   Chris Mason   Btrfs: Fix checkp...
27

b4ce94de9   Chris Mason   Btrfs: Change btr...
28
  /*
bd681513f   Chris Mason   Btrfs: switch the...
29
30
31
   * if we currently have a spinning reader or writer lock
   * (indicated by the rw flag) this will bump the count
   * of blocking holders and drop the spinlock.
b4ce94de9   Chris Mason   Btrfs: Change btr...
32
   */
bd681513f   Chris Mason   Btrfs: switch the...
33
  void btrfs_set_lock_blocking_rw(struct extent_buffer *eb, int rw)
925baeddc   Chris Mason   Btrfs: Start btre...
34
  {
bd681513f   Chris Mason   Btrfs: switch the...
35
36
37
38
39
40
41
42
43
44
45
46
47
48
  	if (rw == BTRFS_WRITE_LOCK) {
  		if (atomic_read(&eb->blocking_writers) == 0) {
  			WARN_ON(atomic_read(&eb->spinning_writers) != 1);
  			atomic_dec(&eb->spinning_writers);
  			btrfs_assert_tree_locked(eb);
  			atomic_inc(&eb->blocking_writers);
  			write_unlock(&eb->lock);
  		}
  	} else if (rw == BTRFS_READ_LOCK) {
  		btrfs_assert_tree_read_locked(eb);
  		atomic_inc(&eb->blocking_readers);
  		WARN_ON(atomic_read(&eb->spinning_readers) == 0);
  		atomic_dec(&eb->spinning_readers);
  		read_unlock(&eb->lock);
b4ce94de9   Chris Mason   Btrfs: Change btr...
49
  	}
bd681513f   Chris Mason   Btrfs: switch the...
50
  	return;
b4ce94de9   Chris Mason   Btrfs: Change btr...
51
  }
f9efa9c78   Chris Mason   Btrfs: Reduce con...
52

b4ce94de9   Chris Mason   Btrfs: Change btr...
53
  /*
bd681513f   Chris Mason   Btrfs: switch the...
54
55
   * if we currently have a blocking lock, take the spinlock
   * and drop our blocking count
b4ce94de9   Chris Mason   Btrfs: Change btr...
56
   */
bd681513f   Chris Mason   Btrfs: switch the...
57
  void btrfs_clear_lock_blocking_rw(struct extent_buffer *eb, int rw)
b4ce94de9   Chris Mason   Btrfs: Change btr...
58
  {
bd681513f   Chris Mason   Btrfs: switch the...
59
60
61
62
63
64
65
66
67
68
69
70
71
  	if (rw == BTRFS_WRITE_LOCK_BLOCKING) {
  		BUG_ON(atomic_read(&eb->blocking_writers) != 1);
  		write_lock(&eb->lock);
  		WARN_ON(atomic_read(&eb->spinning_writers));
  		atomic_inc(&eb->spinning_writers);
  		if (atomic_dec_and_test(&eb->blocking_writers))
  			wake_up(&eb->write_lock_wq);
  	} else if (rw == BTRFS_READ_LOCK_BLOCKING) {
  		BUG_ON(atomic_read(&eb->blocking_readers) == 0);
  		read_lock(&eb->lock);
  		atomic_inc(&eb->spinning_readers);
  		if (atomic_dec_and_test(&eb->blocking_readers))
  			wake_up(&eb->read_lock_wq);
b4ce94de9   Chris Mason   Btrfs: Change btr...
72
  	}
bd681513f   Chris Mason   Btrfs: switch the...
73
  	return;
b4ce94de9   Chris Mason   Btrfs: Change btr...
74
75
76
  }
  
  /*
bd681513f   Chris Mason   Btrfs: switch the...
77
78
   * take a spinning read lock.  This will wait for any blocking
   * writers
b4ce94de9   Chris Mason   Btrfs: Change btr...
79
   */
bd681513f   Chris Mason   Btrfs: switch the...
80
  void btrfs_tree_read_lock(struct extent_buffer *eb)
b4ce94de9   Chris Mason   Btrfs: Change btr...
81
  {
bd681513f   Chris Mason   Btrfs: switch the...
82
83
84
85
86
87
88
89
  again:
  	wait_event(eb->write_lock_wq, atomic_read(&eb->blocking_writers) == 0);
  	read_lock(&eb->lock);
  	if (atomic_read(&eb->blocking_writers)) {
  		read_unlock(&eb->lock);
  		wait_event(eb->write_lock_wq,
  			   atomic_read(&eb->blocking_writers) == 0);
  		goto again;
b4ce94de9   Chris Mason   Btrfs: Change btr...
90
  	}
bd681513f   Chris Mason   Btrfs: switch the...
91
92
  	atomic_inc(&eb->read_locks);
  	atomic_inc(&eb->spinning_readers);
b4ce94de9   Chris Mason   Btrfs: Change btr...
93
94
95
  }
  
  /*
bd681513f   Chris Mason   Btrfs: switch the...
96
97
   * returns 1 if we get the read lock and 0 if we don't
   * this won't wait for blocking writers
b4ce94de9   Chris Mason   Btrfs: Change btr...
98
   */
bd681513f   Chris Mason   Btrfs: switch the...
99
  int btrfs_try_tree_read_lock(struct extent_buffer *eb)
b4ce94de9   Chris Mason   Btrfs: Change btr...
100
  {
bd681513f   Chris Mason   Btrfs: switch the...
101
102
  	if (atomic_read(&eb->blocking_writers))
  		return 0;
b4ce94de9   Chris Mason   Btrfs: Change btr...
103

bd681513f   Chris Mason   Btrfs: switch the...
104
105
106
107
  	read_lock(&eb->lock);
  	if (atomic_read(&eb->blocking_writers)) {
  		read_unlock(&eb->lock);
  		return 0;
b9473439d   Chris Mason   Btrfs: leave btre...
108
  	}
bd681513f   Chris Mason   Btrfs: switch the...
109
110
111
  	atomic_inc(&eb->read_locks);
  	atomic_inc(&eb->spinning_readers);
  	return 1;
b4ce94de9   Chris Mason   Btrfs: Change btr...
112
113
114
  }
  
  /*
bd681513f   Chris Mason   Btrfs: switch the...
115
116
   * returns 1 if we get the read lock and 0 if we don't
   * this won't wait for blocking writers or readers
b4ce94de9   Chris Mason   Btrfs: Change btr...
117
   */
bd681513f   Chris Mason   Btrfs: switch the...
118
  int btrfs_try_tree_write_lock(struct extent_buffer *eb)
b4ce94de9   Chris Mason   Btrfs: Change btr...
119
  {
bd681513f   Chris Mason   Btrfs: switch the...
120
121
122
123
124
125
126
127
128
129
130
  	if (atomic_read(&eb->blocking_writers) ||
  	    atomic_read(&eb->blocking_readers))
  		return 0;
  	write_lock(&eb->lock);
  	if (atomic_read(&eb->blocking_writers) ||
  	    atomic_read(&eb->blocking_readers)) {
  		write_unlock(&eb->lock);
  		return 0;
  	}
  	atomic_inc(&eb->write_locks);
  	atomic_inc(&eb->spinning_writers);
b4ce94de9   Chris Mason   Btrfs: Change btr...
131
132
133
134
  	return 1;
  }
  
  /*
bd681513f   Chris Mason   Btrfs: switch the...
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
   * drop a spinning read lock
   */
  void btrfs_tree_read_unlock(struct extent_buffer *eb)
  {
  	btrfs_assert_tree_read_locked(eb);
  	WARN_ON(atomic_read(&eb->spinning_readers) == 0);
  	atomic_dec(&eb->spinning_readers);
  	atomic_dec(&eb->read_locks);
  	read_unlock(&eb->lock);
  }
  
  /*
   * drop a blocking read lock
   */
  void btrfs_tree_read_unlock_blocking(struct extent_buffer *eb)
  {
  	btrfs_assert_tree_read_locked(eb);
  	WARN_ON(atomic_read(&eb->blocking_readers) == 0);
  	if (atomic_dec_and_test(&eb->blocking_readers))
  		wake_up(&eb->read_lock_wq);
  	atomic_dec(&eb->read_locks);
  }
  
  /*
   * take a spinning write lock.  This will wait for both
   * blocking readers or writers
b4ce94de9   Chris Mason   Btrfs: Change btr...
161
162
163
   */
  int btrfs_tree_lock(struct extent_buffer *eb)
  {
bd681513f   Chris Mason   Btrfs: switch the...
164
165
166
167
168
169
170
171
172
  again:
  	wait_event(eb->read_lock_wq, atomic_read(&eb->blocking_readers) == 0);
  	wait_event(eb->write_lock_wq, atomic_read(&eb->blocking_writers) == 0);
  	write_lock(&eb->lock);
  	if (atomic_read(&eb->blocking_readers)) {
  		write_unlock(&eb->lock);
  		wait_event(eb->read_lock_wq,
  			   atomic_read(&eb->blocking_readers) == 0);
  		goto again;
f9efa9c78   Chris Mason   Btrfs: Reduce con...
173
  	}
bd681513f   Chris Mason   Btrfs: switch the...
174
175
176
177
178
179
180
181
182
  	if (atomic_read(&eb->blocking_writers)) {
  		write_unlock(&eb->lock);
  		wait_event(eb->write_lock_wq,
  			   atomic_read(&eb->blocking_writers) == 0);
  		goto again;
  	}
  	WARN_ON(atomic_read(&eb->spinning_writers));
  	atomic_inc(&eb->spinning_writers);
  	atomic_inc(&eb->write_locks);
925baeddc   Chris Mason   Btrfs: Start btre...
183
184
  	return 0;
  }
bd681513f   Chris Mason   Btrfs: switch the...
185
186
187
  /*
   * drop a spinning or a blocking write lock.
   */
925baeddc   Chris Mason   Btrfs: Start btre...
188
189
  int btrfs_tree_unlock(struct extent_buffer *eb)
  {
bd681513f   Chris Mason   Btrfs: switch the...
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
  	int blockers = atomic_read(&eb->blocking_writers);
  
  	BUG_ON(blockers > 1);
  
  	btrfs_assert_tree_locked(eb);
  	atomic_dec(&eb->write_locks);
  
  	if (blockers) {
  		WARN_ON(atomic_read(&eb->spinning_writers));
  		atomic_dec(&eb->blocking_writers);
  		smp_wmb();
  		wake_up(&eb->write_lock_wq);
  	} else {
  		WARN_ON(atomic_read(&eb->spinning_writers) != 1);
  		atomic_dec(&eb->spinning_writers);
  		write_unlock(&eb->lock);
  	}
925baeddc   Chris Mason   Btrfs: Start btre...
207
208
  	return 0;
  }
b9447ef80   Chris Mason   Btrfs: fix spinlo...
209
  void btrfs_assert_tree_locked(struct extent_buffer *eb)
925baeddc   Chris Mason   Btrfs: Start btre...
210
  {
bd681513f   Chris Mason   Btrfs: switch the...
211
212
213
214
215
216
  	BUG_ON(!atomic_read(&eb->write_locks));
  }
  
  void btrfs_assert_tree_read_locked(struct extent_buffer *eb)
  {
  	BUG_ON(!atomic_read(&eb->read_locks));
925baeddc   Chris Mason   Btrfs: Start btre...
217
  }