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