1/* 2 * Copyright (C) 2008 Oracle. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public 6 * License v2 as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 * General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public 14 * License along with this program; if not, write to the 15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 16 * Boston, MA 021110-1307, USA. 17 */ 18#include <linux/sched.h> 19#include <linux/pagemap.h> 20#include <linux/spinlock.h> 21#include <linux/page-flags.h> 22#include <asm/bug.h> 23#include "ctree.h" 24#include "extent_io.h" 25#include "locking.h" 26 27void btrfs_assert_tree_read_locked(struct extent_buffer *eb); 28 29/* 30 * if we currently have a spinning reader or writer lock 31 * (indicated by the rw flag) this will bump the count 32 * of blocking holders and drop the spinlock. 33 */ 34void btrfs_set_lock_blocking_rw(struct extent_buffer *eb, int rw) 35{ 36 if (eb->lock_nested) { 37 read_lock(&eb->lock); 38 if (eb->lock_nested && current->pid == eb->lock_owner) { 39 read_unlock(&eb->lock); 40 return; 41 } 42 read_unlock(&eb->lock); 43 } 44 if (rw == BTRFS_WRITE_LOCK) { 45 if (atomic_read(&eb->blocking_writers) == 0) { 46 WARN_ON(atomic_read(&eb->spinning_writers) != 1); 47 atomic_dec(&eb->spinning_writers); 48 btrfs_assert_tree_locked(eb); 49 atomic_inc(&eb->blocking_writers); 50 write_unlock(&eb->lock); 51 } 52 } else if (rw == BTRFS_READ_LOCK) { 53 btrfs_assert_tree_read_locked(eb); 54 atomic_inc(&eb->blocking_readers); 55 WARN_ON(atomic_read(&eb->spinning_readers) == 0); 56 atomic_dec(&eb->spinning_readers); 57 read_unlock(&eb->lock); 58 } 59 return; 60} 61 62/* 63 * if we currently have a blocking lock, take the spinlock 64 * and drop our blocking count 65 */ 66void btrfs_clear_lock_blocking_rw(struct extent_buffer *eb, int rw) 67{ 68 if (eb->lock_nested) { 69 read_lock(&eb->lock); 70 if (eb->lock_nested && current->pid == eb->lock_owner) { 71 read_unlock(&eb->lock); 72 return; 73 } 74 read_unlock(&eb->lock); 75 } 76 if (rw == BTRFS_WRITE_LOCK_BLOCKING) { 77 BUG_ON(atomic_read(&eb->blocking_writers) != 1); 78 write_lock(&eb->lock); 79 WARN_ON(atomic_read(&eb->spinning_writers)); 80 atomic_inc(&eb->spinning_writers); 81 if (atomic_dec_and_test(&eb->blocking_writers) && 82 waitqueue_active(&eb->write_lock_wq)) 83 wake_up(&eb->write_lock_wq); 84 } else if (rw == BTRFS_READ_LOCK_BLOCKING) { 85 BUG_ON(atomic_read(&eb->blocking_readers) == 0); 86 read_lock(&eb->lock); 87 atomic_inc(&eb->spinning_readers); 88 if (atomic_dec_and_test(&eb->blocking_readers) && 89 waitqueue_active(&eb->read_lock_wq)) 90 wake_up(&eb->read_lock_wq); 91 } 92 return; 93} 94 95/* 96 * take a spinning read lock. This will wait for any blocking 97 * writers 98 */ 99void btrfs_tree_read_lock(struct extent_buffer *eb) 100{ 101again: 102 read_lock(&eb->lock); 103 if (atomic_read(&eb->blocking_writers) && 104 current->pid == eb->lock_owner) { 105 /* 106 * This extent is already write-locked by our thread. We allow 107 * an additional read lock to be added because it's for the same 108 * thread. btrfs_find_all_roots() depends on this as it may be 109 * called on a partly (write-)locked tree. 110 */ 111 BUG_ON(eb->lock_nested); 112 eb->lock_nested = 1; 113 read_unlock(&eb->lock); 114 return; 115 } 116 if (atomic_read(&eb->blocking_writers)) { 117 read_unlock(&eb->lock); 118 wait_event(eb->write_lock_wq, 119 atomic_read(&eb->blocking_writers) == 0); 120 goto again; 121 } 122 atomic_inc(&eb->read_locks); 123 atomic_inc(&eb->spinning_readers); 124} 125 126/* 127 * returns 1 if we get the read lock and 0 if we don't 128 * this won't wait for blocking writers 129 */ 130int btrfs_try_tree_read_lock(struct extent_buffer *eb) 131{ 132 if (atomic_read(&eb->blocking_writers)) 133 return 0; 134 135 read_lock(&eb->lock); 136 if (atomic_read(&eb->blocking_writers)) { 137 read_unlock(&eb->lock); 138 return 0; 139 } 140 atomic_inc(&eb->read_locks); 141 atomic_inc(&eb->spinning_readers); 142 return 1; 143} 144 145/* 146 * returns 1 if we get the read lock and 0 if we don't 147 * this won't wait for blocking writers or readers 148 */ 149int btrfs_try_tree_write_lock(struct extent_buffer *eb) 150{ 151 if (atomic_read(&eb->blocking_writers) || 152 atomic_read(&eb->blocking_readers)) 153 return 0; 154 write_lock(&eb->lock); 155 if (atomic_read(&eb->blocking_writers) || 156 atomic_read(&eb->blocking_readers)) { 157 write_unlock(&eb->lock); 158 return 0; 159 } 160 atomic_inc(&eb->write_locks); 161 atomic_inc(&eb->spinning_writers); 162 eb->lock_owner = current->pid; 163 return 1; 164} 165 166/* 167 * drop a spinning read lock 168 */ 169void btrfs_tree_read_unlock(struct extent_buffer *eb) 170{ 171 if (eb->lock_nested) { 172 read_lock(&eb->lock); 173 if (eb->lock_nested && current->pid == eb->lock_owner) { 174 eb->lock_nested = 0; 175 read_unlock(&eb->lock); 176 return; 177 } 178 read_unlock(&eb->lock); 179 } 180 btrfs_assert_tree_read_locked(eb); 181 WARN_ON(atomic_read(&eb->spinning_readers) == 0); 182 atomic_dec(&eb->spinning_readers); 183 atomic_dec(&eb->read_locks); 184 read_unlock(&eb->lock); 185} 186 187/* 188 * drop a blocking read lock 189 */ 190void btrfs_tree_read_unlock_blocking(struct extent_buffer *eb) 191{ 192 if (eb->lock_nested) { 193 read_lock(&eb->lock); 194 if (eb->lock_nested && current->pid == eb->lock_owner) { 195 eb->lock_nested = 0; 196 read_unlock(&eb->lock); 197 return; 198 } 199 read_unlock(&eb->lock); 200 } 201 btrfs_assert_tree_read_locked(eb); 202 WARN_ON(atomic_read(&eb->blocking_readers) == 0); 203 if (atomic_dec_and_test(&eb->blocking_readers) && 204 waitqueue_active(&eb->read_lock_wq)) 205 wake_up(&eb->read_lock_wq); 206 atomic_dec(&eb->read_locks); 207} 208 209/* 210 * take a spinning write lock. This will wait for both 211 * blocking readers or writers 212 */ 213void btrfs_tree_lock(struct extent_buffer *eb) 214{ 215again: 216 wait_event(eb->read_lock_wq, atomic_read(&eb->blocking_readers) == 0); 217 wait_event(eb->write_lock_wq, atomic_read(&eb->blocking_writers) == 0); 218 write_lock(&eb->lock); 219 if (atomic_read(&eb->blocking_readers)) { 220 write_unlock(&eb->lock); 221 wait_event(eb->read_lock_wq, 222 atomic_read(&eb->blocking_readers) == 0); 223 goto again; 224 } 225 if (atomic_read(&eb->blocking_writers)) { 226 write_unlock(&eb->lock); 227 wait_event(eb->write_lock_wq, 228 atomic_read(&eb->blocking_writers) == 0); 229 goto again; 230 } 231 WARN_ON(atomic_read(&eb->spinning_writers)); 232 atomic_inc(&eb->spinning_writers); 233 atomic_inc(&eb->write_locks); 234 eb->lock_owner = current->pid; 235} 236 237/* 238 * drop a spinning or a blocking write lock. 239 */ 240void btrfs_tree_unlock(struct extent_buffer *eb) 241{ 242 int blockers = atomic_read(&eb->blocking_writers); 243 244 BUG_ON(blockers > 1); 245 246 btrfs_assert_tree_locked(eb); 247 atomic_dec(&eb->write_locks); 248 249 if (blockers) { 250 WARN_ON(atomic_read(&eb->spinning_writers)); 251 atomic_dec(&eb->blocking_writers); 252 smp_mb(); 253 if (waitqueue_active(&eb->write_lock_wq)) 254 wake_up(&eb->write_lock_wq); 255 } else { 256 WARN_ON(atomic_read(&eb->spinning_writers) != 1); 257 atomic_dec(&eb->spinning_writers); 258 write_unlock(&eb->lock); 259 } 260} 261 262void btrfs_assert_tree_locked(struct extent_buffer *eb) 263{ 264 BUG_ON(!atomic_read(&eb->write_locks)); 265} 266 267void btrfs_assert_tree_read_locked(struct extent_buffer *eb) 268{ 269 BUG_ON(!atomic_read(&eb->read_locks)); 270} 271