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