vfs_trans.c revision 1.2
1/* $NetBSD: vfs_trans.c,v 1.2 2007/01/29 15:42:50 hannken 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.2 2007/01/29 15:42:50 hannken 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/vnode.h> 57#define _FSTRANS_API_PRIVATE 58#include <sys/fstrans.h> 59 60#include <miscfs/syncfs/syncfs.h> 61 62struct fstrans_lwp_info { 63 struct fstrans_lwp_info *fli_succ; 64 struct mount *fli_mount; 65 int fli_count; 66 enum fstrans_lock_type fli_lock_type; 67}; 68struct fstrans_mount_info { 69 enum fstrans_state fmi_state; 70 struct lock fmi_shared_lock; 71 struct lock fmi_lazy_lock; 72}; 73 74static specificdata_key_t lwp_data_key; 75static specificdata_key_t mount_data_key; 76static struct lock vfs_suspend_lock = /* Serialize suspensions. */ 77 LOCK_INITIALIZER(PUSER, "suspwt1", 0, 0); 78 79POOL_INIT(fstrans_pl, sizeof(struct fstrans_lwp_info), 0, 0, 0, 80 "fstrans", NULL); 81 82static void fstrans_lwp_dtor(void *); 83static void fstrans_mount_dtor(void *); 84static struct fstrans_mount_info *fstrans_mount_init(struct mount *); 85 86/* 87 * Initialize 88 */ 89void 90fstrans_init(void) 91{ 92 int error; 93 94 error = lwp_specific_key_create(&lwp_data_key, fstrans_lwp_dtor); 95 KASSERT(error == 0); 96 error = mount_specific_key_create(&mount_data_key, fstrans_mount_dtor); 97 KASSERT(error == 0); 98} 99 100/* 101 * Deallocate lwp state 102 */ 103static void 104fstrans_lwp_dtor(void *arg) 105{ 106 struct fstrans_lwp_info *fli, *fli_next; 107 108 for (fli = arg; fli; fli = fli_next) { 109 KASSERT(fli->fli_mount == NULL); 110 KASSERT(fli->fli_count == 0); 111 fli_next = fli->fli_succ; 112 pool_put(&fstrans_pl, fli); 113 } 114} 115 116/* 117 * Deallocate mount state 118 */ 119static void 120fstrans_mount_dtor(void *arg) 121{ 122 struct fstrans_mount_info *fmi = arg; 123 124 KASSERT(fmi->fmi_state == FSTRANS_NORMAL); 125 lockmgr(&fmi->fmi_lazy_lock, LK_DRAIN, NULL); 126 lockmgr(&fmi->fmi_shared_lock, LK_DRAIN, NULL); 127 free(fmi, M_MOUNT); 128} 129 130/* 131 * Create mount info for this mount 132 */ 133static struct fstrans_mount_info * 134fstrans_mount_init(struct mount *mp) 135{ 136 static struct lock lock = LOCK_INITIALIZER(PUSER, "ftminfo", 0, 0); 137 struct fstrans_mount_info *new; 138 139 lockmgr(&lock, LK_EXCLUSIVE, NULL); 140 141 if ((new = mount_getspecific(mp, mount_data_key)) != NULL) { 142 lockmgr(&lock, LK_RELEASE, NULL); 143 return new; 144 } 145 146 new = malloc(sizeof(*new), M_MOUNT, M_WAITOK); 147 new->fmi_state = FSTRANS_NORMAL; 148 lockinit(&new->fmi_lazy_lock, PVFS, "suspfs", 0, 0); 149 lockinit(&new->fmi_shared_lock, PVFS, "suspfs", 0, 0); 150 151 mount_setspecific(mp, mount_data_key, new); 152 153 lockmgr(&lock, LK_RELEASE, NULL); 154 155 return new; 156} 157 158/* 159 * Start a transaction. If this thread already has a transaction on this 160 * file system increment the reference counter. 161 * A thread with an exclusive transaction lock may get a shared or lazy one. 162 * A thread with a shared or lazy transaction lock cannot upgrade to an 163 * exclusive one yet. 164 */ 165int 166_fstrans_start(struct mount *mp, enum fstrans_lock_type lock_type, int wait) 167{ 168 int error, lkflags; 169 struct fstrans_lwp_info *fli, *new_fli; 170 struct fstrans_mount_info *fmi; 171 172 ASSERT_SLEEPABLE(NULL, __func__); 173 174 lkflags = (lock_type == FSTRANS_EXCL ? LK_EXCLUSIVE : LK_SHARED); 175 if (!wait) 176 lkflags |= LK_NOWAIT; 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 error = lockmgr(&fmi->fmi_lazy_lock, lkflags, NULL); 211 else 212 error = lockmgr(&fmi->fmi_shared_lock, lkflags, NULL); 213 if (error) 214 return error; 215 216 new_fli->fli_mount = mp; 217 new_fli->fli_count = 1; 218 new_fli->fli_lock_type = lock_type; 219 220 return 0; 221} 222 223/* 224 * Finish a transaction. 225 */ 226void 227fstrans_done(struct mount *mp) 228{ 229 struct fstrans_lwp_info *fli; 230 struct fstrans_mount_info *fmi; 231 232 if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0) 233 return; 234 235 for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) { 236 if (fli->fli_mount == mp) { 237 fli->fli_count -= 1; 238 if (fli->fli_count > 0) 239 return; 240 break; 241 } 242 } 243 244 KASSERT(fli != NULL); 245 KASSERT(fli->fli_mount == mp); 246 KASSERT(fli->fli_count == 0); 247 fli->fli_mount = NULL; 248 fmi = mount_getspecific(mp, mount_data_key); 249 KASSERT(fmi != NULL); 250 if (fli->fli_lock_type == FSTRANS_LAZY) 251 lockmgr(&fmi->fmi_lazy_lock, LK_RELEASE, NULL); 252 else 253 lockmgr(&fmi->fmi_shared_lock, LK_RELEASE, NULL); 254} 255 256/* 257 * Check if this thread has an exclusive lock. 258 */ 259int 260fstrans_is_owner(struct mount *mp) 261{ 262 struct fstrans_lwp_info *fli; 263 264 if (mp == NULL) 265 return 0; 266 if ((mp->mnt_iflag & IMNT_HAS_TRANS) == 0) 267 return 0; 268 269 for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) 270 if (fli->fli_mount == mp) 271 break; 272 273 if (fli == NULL) 274 return 0; 275 276 KASSERT(fli->fli_mount == mp); 277 KASSERT(fli->fli_count > 0); 278 return (fli->fli_lock_type == FSTRANS_EXCL); 279} 280 281/* 282 * Set new file system state. 283 */ 284int 285fstrans_setstate(struct mount *mp, enum fstrans_state new_state) 286{ 287 int error; 288 struct fstrans_mount_info *fmi; 289 290 if ((fmi = mount_getspecific(mp, mount_data_key)) == NULL) 291 fmi = fstrans_mount_init(mp); 292 293 switch (new_state) { 294 case FSTRANS_SUSPENDING: 295 KASSERT(fmi->fmi_state == FSTRANS_NORMAL); 296 if ((error = fstrans_start(mp, FSTRANS_EXCL)) != 0) 297 return error; 298 fmi->fmi_state = FSTRANS_SUSPENDING; 299 break; 300 301 case FSTRANS_SUSPENDED: 302 KASSERT(fmi->fmi_state == FSTRANS_NORMAL || 303 fmi->fmi_state == FSTRANS_SUSPENDING); 304 KASSERT(fmi->fmi_state == FSTRANS_NORMAL || 305 fstrans_is_owner(mp)); 306 if (fmi->fmi_state == FSTRANS_NORMAL && 307 (error = fstrans_start(mp, FSTRANS_EXCL)) != 0) 308 return error; 309 if ((error = lockmgr(&fmi->fmi_lazy_lock, LK_EXCLUSIVE, NULL)) 310 != 0) 311 return error; 312 fmi->fmi_state = FSTRANS_SUSPENDED; 313 break; 314 315 case FSTRANS_NORMAL: 316 KASSERT(fmi->fmi_state == FSTRANS_NORMAL || 317 fstrans_is_owner(mp)); 318 if (fmi->fmi_state == FSTRANS_SUSPENDED) 319 lockmgr(&fmi->fmi_lazy_lock, LK_RELEASE, NULL); 320 if (fmi->fmi_state == FSTRANS_SUSPENDING || 321 fmi->fmi_state == FSTRANS_SUSPENDED) { 322 fmi->fmi_state = FSTRANS_NORMAL; 323 fstrans_done(mp); 324 } 325 break; 326 327 default: 328 panic("%s: illegal state %d", __func__, new_state); 329 } 330 331 return 0; 332} 333 334/* 335 * Get current file system state 336 */ 337enum fstrans_state 338fstrans_getstate(struct mount *mp) 339{ 340 struct fstrans_mount_info *fmi; 341 342 if ((fmi = mount_getspecific(mp, mount_data_key)) == NULL) 343 return FSTRANS_NORMAL; 344 345 return fmi->fmi_state; 346} 347 348/* 349 * Request a filesystem to suspend all operations. 350 */ 351int 352vfs_suspend(struct mount *mp, int nowait) 353{ 354#ifndef NEWVNGATE 355 struct lwp *l = curlwp; /* XXX */ 356#endif /* NEWVNGATE */ 357 int error, flags; 358 359 flags = LK_EXCLUSIVE; 360 if (nowait) 361 flags |= LK_NOWAIT; 362 if (lockmgr(&vfs_suspend_lock, flags, NULL) != 0) 363 return EWOULDBLOCK; 364 365#ifdef NEWVNGATE 366 lockmgr(&syncer_lock, LK_EXCLUSIVE, NULL); 367 368 if ((error = VFS_SUSPENDCTL(mp, SUSPEND_SUSPEND)) != 0) { 369 lockmgr(&syncer_lock, LK_RELEASE, NULL); 370 lockmgr(&vfs_suspend_lock, LK_RELEASE, NULL); 371 } 372 373 return error; 374#else /* NEWVNGATE */ 375 KASSERT((mp->mnt_iflag & 376 (IMNT_SUSPEND|IMNT_SUSPENDLOW|IMNT_SUSPENDED)) == 0); 377 378 mp->mnt_iflag |= IMNT_SUSPEND; 379 380 simple_lock(&mp->mnt_slock); 381 if (mp->mnt_writeopcountupper > 0) 382 ltsleep(&mp->mnt_writeopcountupper, PUSER - 1, "suspwt", 383 0, &mp->mnt_slock); 384 simple_unlock(&mp->mnt_slock); 385 386 error = VFS_SYNC(mp, MNT_WAIT, l->l_proc->p_cred, l); 387 if (error) { 388 vfs_resume(mp); 389 return error; 390 } 391 mp->mnt_iflag |= IMNT_SUSPENDLOW; 392 393 simple_lock(&mp->mnt_slock); 394 if (mp->mnt_writeopcountlower > 0) 395 ltsleep(&mp->mnt_writeopcountlower, PUSER - 1, "suspwt", 396 0, &mp->mnt_slock); 397 mp->mnt_iflag |= IMNT_SUSPENDED; 398 simple_unlock(&mp->mnt_slock); 399 400 return 0; 401#endif /* NEWVNGATE */ 402} 403 404/* 405 * Request a filesystem to resume all operations. 406 */ 407void 408vfs_resume(struct mount *mp) 409{ 410 411#ifdef NEWVNGATE 412 VFS_SUSPENDCTL(mp, SUSPEND_RESUME); 413 lockmgr(&syncer_lock, LK_RELEASE, NULL); 414 lockmgr(&vfs_suspend_lock, LK_RELEASE, NULL); 415#else /* NEWVNGATE */ 416 if ((mp->mnt_iflag & IMNT_SUSPEND) == 0) 417 return; 418 mp->mnt_iflag &= ~(IMNT_SUSPEND | IMNT_SUSPENDLOW | IMNT_SUSPENDED); 419 wakeup(&mp->mnt_flag); 420 421 lockmgr(&vfs_suspend_lock, LK_RELEASE, NULL); 422#endif /* NEWVNGATE */ 423} 424 425/* 426 * Default vfs_suspendctl routine for file systems that do not support it. 427 */ 428/*ARGSUSED*/ 429int 430vfs_stdsuspendctl(struct mount *mp __unused, int mode __unused) 431{ 432 return EOPNOTSUPP; 433} 434 435#if defined(DDB) 436void fstrans_dump(int); 437 438static void 439fstrans_print_lwp(struct proc *p, struct lwp *l, int verbose) 440{ 441 char prefix[9]; 442 struct fstrans_lwp_info *fli; 443 444 snprintf(prefix, sizeof(prefix), "%d.%d", p->p_pid, l->l_lid); 445 for (fli = _lwp_getspecific_by_lwp(l, lwp_data_key); 446 fli; 447 fli = fli->fli_succ) { 448 if (!verbose && fli->fli_count == 0) 449 continue; 450 printf("%-8s", prefix); 451 if (verbose) 452 printf(" @%p", fli); 453 if (fli->fli_mount != NULL) 454 printf(" (%s)", fli->fli_mount->mnt_stat.f_mntonname); 455 else 456 printf(" NULL"); 457 switch (fli->fli_lock_type) { 458 case FSTRANS_LAZY: 459 printf(" lazy"); 460 break; 461 case FSTRANS_SHARED: 462 printf(" shared"); 463 break; 464 case FSTRANS_EXCL: 465 printf(" excl"); 466 break; 467 default: 468 printf(" %#x", fli->fli_lock_type); 469 break; 470 } 471 printf(" %d\n", fli->fli_count); 472 prefix[0] = '\0'; 473 } 474} 475 476static void 477fstrans_print_mount(struct mount *mp, int verbose) 478{ 479 struct fstrans_mount_info *fmi; 480 481 fmi = mount_getspecific(mp, mount_data_key); 482 if (!verbose && (fmi == NULL || fmi->fmi_state == FSTRANS_NORMAL)) 483 return; 484 485 printf("%-16s ", mp->mnt_stat.f_mntonname); 486 if (fmi == NULL) { 487 printf("(null)\n"); 488 return; 489 } 490 switch (fmi->fmi_state) { 491 case FSTRANS_NORMAL: 492 printf("state normal\n"); 493 break; 494 case FSTRANS_SUSPENDING: 495 printf("state suspending\n"); 496 break; 497 case FSTRANS_SUSPENDED: 498 printf("state suspended\n"); 499 break; 500 default: 501 printf("state %#x\n", fmi->fmi_state); 502 break; 503 } 504 printf("%16s", "lock_lazy:"); 505 lockmgr_printinfo(&fmi->fmi_lazy_lock); 506 printf("\n"); 507 printf("%16s", "lock_shared:"); 508 lockmgr_printinfo(&fmi->fmi_shared_lock); 509 printf("\n"); 510} 511 512void 513fstrans_dump(int full) 514{ 515 const struct proclist_desc *pd; 516 struct proc *p; 517 struct lwp *l; 518 struct mount *mp; 519 520 printf("Fstrans locks by lwp:\n"); 521 for (pd = proclists; pd->pd_list != NULL; pd++) 522 LIST_FOREACH(p, pd->pd_list, p_list) 523 LIST_FOREACH(l, &p->p_lwps, l_sibling) 524 fstrans_print_lwp(p, l, full == 1); 525 526 printf("Fstrans state by mount:\n"); 527 CIRCLEQ_FOREACH(mp, &mountlist, mnt_list) 528 fstrans_print_mount(mp, full == 1); 529} 530#endif /* defined(DDB) */ 531