vfs_trans.c revision 1.11
1/* $NetBSD: vfs_trans.c,v 1.11 2007/07/26 22:57:36 pooka Exp $ */ 2 3/*- 4 * Copyright (c) 2007 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Juergen Hannken-Illjes. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39#include <sys/cdefs.h> 40__KERNEL_RCSID(0, "$NetBSD: vfs_trans.c,v 1.11 2007/07/26 22:57:36 pooka Exp $"); 41 42/* 43 * File system transaction operations. 44 */ 45 46#include "opt_ddb.h" 47 48#if defined(DDB) 49#define _LWP_API_PRIVATE /* Need _lwp_getspecific_by_lwp() */ 50#endif 51 52#include <sys/param.h> 53#include <sys/systm.h> 54#include <sys/malloc.h> 55#include <sys/mount.h> 56#include <sys/rwlock.h> 57#include <sys/vnode.h> 58#define _FSTRANS_API_PRIVATE 59#include <sys/fstrans.h> 60#include <sys/proc.h> 61 62#include <miscfs/syncfs/syncfs.h> 63 64struct fstrans_lwp_info { 65 struct fstrans_lwp_info *fli_succ; 66 struct mount *fli_mount; 67 int fli_count; 68 enum fstrans_lock_type fli_lock_type; 69}; 70struct fstrans_mount_info { 71 enum fstrans_state fmi_state; 72 krwlock_t fmi_shared_lock; 73 krwlock_t fmi_lazy_lock; 74}; 75 76static specificdata_key_t lwp_data_key; 77static specificdata_key_t mount_data_key; 78static kmutex_t vfs_suspend_lock; /* Serialize suspensions. */ 79static kmutex_t fstrans_init_lock; 80 81POOL_INIT(fstrans_pl, sizeof(struct fstrans_lwp_info), 0, 0, 0, 82 "fstrans", NULL, IPL_NONE); 83 84static void fstrans_lwp_dtor(void *); 85static void fstrans_mount_dtor(void *); 86static struct fstrans_mount_info *fstrans_mount_init(struct mount *); 87 88/* 89 * Initialize 90 */ 91void 92fstrans_init(void) 93{ 94 int error; 95 96 error = lwp_specific_key_create(&lwp_data_key, fstrans_lwp_dtor); 97 KASSERT(error == 0); 98 error = mount_specific_key_create(&mount_data_key, fstrans_mount_dtor); 99 KASSERT(error == 0); 100 101 mutex_init(&vfs_suspend_lock, MUTEX_DEFAULT, IPL_NONE); 102 mutex_init(&fstrans_init_lock, MUTEX_DEFAULT, IPL_NONE); 103} 104 105/* 106 * Deallocate lwp state 107 */ 108static void 109fstrans_lwp_dtor(void *arg) 110{ 111 struct fstrans_lwp_info *fli, *fli_next; 112 113 for (fli = arg; fli; fli = fli_next) { 114 KASSERT(fli->fli_mount == NULL); 115 KASSERT(fli->fli_count == 0); 116 fli_next = fli->fli_succ; 117 pool_put(&fstrans_pl, fli); 118 } 119} 120 121/* 122 * Deallocate mount state 123 */ 124static void 125fstrans_mount_dtor(void *arg) 126{ 127 struct fstrans_mount_info *fmi = arg; 128 129 KASSERT(fmi->fmi_state == FSTRANS_NORMAL); 130 rw_destroy(&fmi->fmi_lazy_lock); 131 rw_destroy(&fmi->fmi_shared_lock); 132 free(fmi, M_MOUNT); 133} 134 135/* 136 * Create mount info for this mount 137 */ 138static struct fstrans_mount_info * 139fstrans_mount_init(struct mount *mp) 140{ 141 struct fstrans_mount_info *new; 142 143 mutex_enter(&fstrans_init_lock); 144 145 if ((new = mount_getspecific(mp, mount_data_key)) != NULL) { 146 mutex_exit(&fstrans_init_lock); 147 return new; 148 } 149 150 new = malloc(sizeof(*new), M_MOUNT, M_WAITOK); 151 new->fmi_state = FSTRANS_NORMAL; 152 rw_init(&new->fmi_lazy_lock); 153 rw_init(&new->fmi_shared_lock); 154 155 mount_setspecific(mp, mount_data_key, new); 156 mutex_exit(&fstrans_init_lock); 157 158 return new; 159} 160 161/* 162 * Start a transaction. If this thread already has a transaction on this 163 * file system increment the reference counter. 164 * A thread with an exclusive transaction lock may get a shared or lazy one. 165 * A thread with a shared or lazy transaction lock cannot upgrade to an 166 * exclusive one yet. 167 */ 168int 169_fstrans_start(struct mount *mp, enum fstrans_lock_type lock_type, int wait) 170{ 171 krwlock_t *lock_p; 172 krw_t lock_op; 173 struct fstrans_lwp_info *fli, *new_fli; 174 struct fstrans_mount_info *fmi; 175 176 ASSERT_SLEEPABLE(NULL, __func__); 177 178 if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0) 179 return 0; 180 181 new_fli = NULL; 182 for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) { 183 if (fli->fli_mount == NULL && new_fli == NULL) 184 new_fli = fli; 185 if (fli->fli_mount == mp) { 186 KASSERT(fli->fli_count > 0); 187 if (fli->fli_lock_type != FSTRANS_EXCL && 188 lock_type == FSTRANS_EXCL) 189 panic("fstrans_start: cannot upgrade lock"); 190 fli->fli_count += 1; 191 return 0; 192 } 193 } 194 195 if (new_fli == NULL) { 196 new_fli = pool_get(&fstrans_pl, PR_WAITOK); 197 new_fli->fli_mount = NULL; 198 new_fli->fli_count = 0; 199 new_fli->fli_succ = lwp_getspecific(lwp_data_key); 200 lwp_setspecific(lwp_data_key, new_fli); 201 } 202 203 KASSERT(new_fli->fli_mount == NULL); 204 KASSERT(new_fli->fli_count == 0); 205 206 if ((fmi = mount_getspecific(mp, mount_data_key)) == NULL) 207 fmi = fstrans_mount_init(mp); 208 209 if (lock_type == FSTRANS_LAZY) 210 lock_p = &fmi->fmi_lazy_lock; 211 else 212 lock_p = &fmi->fmi_shared_lock; 213 lock_op = (lock_type == FSTRANS_EXCL ? RW_WRITER : RW_READER); 214 215 if (wait) 216 rw_enter(lock_p, lock_op); 217 else if (rw_tryenter(lock_p, lock_op) == 0) 218 return EBUSY; 219 220 new_fli->fli_mount = mp; 221 new_fli->fli_count = 1; 222 new_fli->fli_lock_type = lock_type; 223 224 return 0; 225} 226 227/* 228 * Finish a transaction. 229 */ 230void 231fstrans_done(struct mount *mp) 232{ 233 struct fstrans_lwp_info *fli; 234 struct fstrans_mount_info *fmi; 235 236 if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0) 237 return; 238 239 for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) { 240 if (fli->fli_mount == mp) { 241 fli->fli_count -= 1; 242 if (fli->fli_count > 0) 243 return; 244 break; 245 } 246 } 247 248 KASSERT(fli != NULL); 249 KASSERT(fli->fli_mount == mp); 250 KASSERT(fli->fli_count == 0); 251 fli->fli_mount = NULL; 252 fmi = mount_getspecific(mp, mount_data_key); 253 KASSERT(fmi != NULL); 254 if (fli->fli_lock_type == FSTRANS_LAZY) 255 rw_exit(&fmi->fmi_lazy_lock); 256 else 257 rw_exit(&fmi->fmi_shared_lock); 258} 259 260/* 261 * Check if this thread has an exclusive lock. 262 */ 263int 264fstrans_is_owner(struct mount *mp) 265{ 266 struct fstrans_lwp_info *fli; 267 268 if (mp == NULL) 269 return 0; 270 if ((mp->mnt_iflag & IMNT_HAS_TRANS) == 0) 271 return 0; 272 273 for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) 274 if (fli->fli_mount == mp) 275 break; 276 277 if (fli == NULL) 278 return 0; 279 280 KASSERT(fli->fli_mount == mp); 281 KASSERT(fli->fli_count > 0); 282 return (fli->fli_lock_type == FSTRANS_EXCL); 283} 284 285/* 286 * Set new file system state. 287 */ 288int 289fstrans_setstate(struct mount *mp, enum fstrans_state new_state) 290{ 291 struct fstrans_mount_info *fmi; 292 293 if ((fmi = mount_getspecific(mp, mount_data_key)) == NULL) 294 fmi = fstrans_mount_init(mp); 295 296 switch (new_state) { 297 case FSTRANS_SUSPENDING: 298 KASSERT(fmi->fmi_state == FSTRANS_NORMAL); 299 fstrans_start(mp, FSTRANS_EXCL); 300 fmi->fmi_state = FSTRANS_SUSPENDING; 301 break; 302 303 case FSTRANS_SUSPENDED: 304 KASSERT(fmi->fmi_state == FSTRANS_NORMAL || 305 fmi->fmi_state == FSTRANS_SUSPENDING); 306 KASSERT(fmi->fmi_state == FSTRANS_NORMAL || 307 fstrans_is_owner(mp)); 308 if (fmi->fmi_state == FSTRANS_NORMAL) 309 fstrans_start(mp, FSTRANS_EXCL); 310 rw_enter(&fmi->fmi_lazy_lock, RW_WRITER); 311 fmi->fmi_state = FSTRANS_SUSPENDED; 312 break; 313 314 case FSTRANS_NORMAL: 315 KASSERT(fmi->fmi_state == FSTRANS_NORMAL || 316 fstrans_is_owner(mp)); 317 if (fmi->fmi_state == FSTRANS_SUSPENDED) 318 rw_exit(&fmi->fmi_lazy_lock); 319 if (fmi->fmi_state == FSTRANS_SUSPENDING || 320 fmi->fmi_state == FSTRANS_SUSPENDED) { 321 fmi->fmi_state = FSTRANS_NORMAL; 322 fstrans_done(mp); 323 } 324 break; 325 326 default: 327 panic("%s: illegal state %d", __func__, new_state); 328 } 329 330 return 0; 331} 332 333/* 334 * Get current file system state 335 */ 336enum fstrans_state 337fstrans_getstate(struct mount *mp) 338{ 339 struct fstrans_mount_info *fmi; 340 341 if ((fmi = mount_getspecific(mp, mount_data_key)) == NULL) 342 return FSTRANS_NORMAL; 343 344 return fmi->fmi_state; 345} 346 347/* 348 * Request a filesystem to suspend all operations. 349 */ 350int 351vfs_suspend(struct mount *mp, int nowait) 352{ 353 int error; 354 355 if (nowait) { 356 if (!mutex_tryenter(&vfs_suspend_lock)) 357 return EWOULDBLOCK; 358 } else 359 mutex_enter(&vfs_suspend_lock); 360 361 mutex_enter(&syncer_mutex); 362 363 if ((error = VFS_SUSPENDCTL(mp, SUSPEND_SUSPEND)) != 0) { 364 mutex_exit(&syncer_mutex); 365 mutex_exit(&vfs_suspend_lock); 366 } 367 368 return error; 369} 370 371/* 372 * Request a filesystem to resume all operations. 373 */ 374void 375vfs_resume(struct mount *mp) 376{ 377 378 VFS_SUSPENDCTL(mp, SUSPEND_RESUME); 379 mutex_exit(&syncer_mutex); 380 mutex_exit(&vfs_suspend_lock); 381} 382 383#if defined(DDB) 384void fstrans_dump(int); 385 386static void 387fstrans_print_lwp(struct proc *p, struct lwp *l, int verbose) 388{ 389 char prefix[9]; 390 struct fstrans_lwp_info *fli; 391 392 snprintf(prefix, sizeof(prefix), "%d.%d", p->p_pid, l->l_lid); 393 for (fli = _lwp_getspecific_by_lwp(l, lwp_data_key); 394 fli; 395 fli = fli->fli_succ) { 396 if (!verbose && fli->fli_count == 0) 397 continue; 398 printf("%-8s", prefix); 399 if (verbose) 400 printf(" @%p", fli); 401 if (fli->fli_mount != NULL) 402 printf(" (%s)", fli->fli_mount->mnt_stat.f_mntonname); 403 else 404 printf(" NULL"); 405 switch (fli->fli_lock_type) { 406 case FSTRANS_LAZY: 407 printf(" lazy"); 408 break; 409 case FSTRANS_SHARED: 410 printf(" shared"); 411 break; 412 case FSTRANS_EXCL: 413 printf(" excl"); 414 break; 415 default: 416 printf(" %#x", fli->fli_lock_type); 417 break; 418 } 419 printf(" %d\n", fli->fli_count); 420 prefix[0] = '\0'; 421 } 422} 423 424static void 425fstrans_print_mount(struct mount *mp, int verbose) 426{ 427 struct fstrans_mount_info *fmi; 428 429 fmi = mount_getspecific(mp, mount_data_key); 430 if (!verbose && (fmi == NULL || fmi->fmi_state == FSTRANS_NORMAL)) 431 return; 432 433 printf("%-16s ", mp->mnt_stat.f_mntonname); 434 if (fmi == NULL) { 435 printf("(null)\n"); 436 return; 437 } 438 switch (fmi->fmi_state) { 439 case FSTRANS_NORMAL: 440 printf("state normal\n"); 441 break; 442 case FSTRANS_SUSPENDING: 443 printf("state suspending\n"); 444 break; 445 case FSTRANS_SUSPENDED: 446 printf("state suspended\n"); 447 break; 448 default: 449 printf("state %#x\n", fmi->fmi_state); 450 break; 451 } 452 printf("%16s r=%d w=%d\n", "lock_lazy:", 453 rw_read_held(&fmi->fmi_lazy_lock), 454 rw_write_held(&fmi->fmi_lazy_lock)); 455 printf("%16s r=%d w=%d\n", "lock_shared:", 456 rw_read_held(&fmi->fmi_shared_lock), 457 rw_write_held(&fmi->fmi_shared_lock)); 458} 459 460void 461fstrans_dump(int full) 462{ 463 const struct proclist_desc *pd; 464 struct proc *p; 465 struct lwp *l; 466 struct mount *mp; 467 468 printf("Fstrans locks by lwp:\n"); 469 for (pd = proclists; pd->pd_list != NULL; pd++) 470 LIST_FOREACH(p, pd->pd_list, p_list) 471 LIST_FOREACH(l, &p->p_lwps, l_sibling) 472 fstrans_print_lwp(p, l, full == 1); 473 474 printf("Fstrans state by mount:\n"); 475 CIRCLEQ_FOREACH(mp, &mountlist, mnt_list) 476 fstrans_print_mount(mp, full == 1); 477} 478#endif /* defined(DDB) */ 479