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