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