Initial attempt at suspend/snapshot support for userspace file
servers. This is still pretty much on the level "if it breaks ...". It should work for single-threaded servers which handle one operation from start to finish in one go. Also, it does not yet totally correctly synchronize metadata and data in some cases. So needless to say, it needs improvement, but it is possible that will have to wait for some lock revampage.
This commit is contained in:
parent
4ee02bdcac
commit
d2595d03c5
@ -1,7 +1,7 @@
|
||||
/* $NetBSD: puffs_msgif.c,v 1.15 2007/01/19 13:01:15 pooka Exp $ */
|
||||
/* $NetBSD: puffs_msgif.c,v 1.16 2007/01/26 22:59:49 pooka Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005, 2006 Antti Kantee. All Rights Reserved.
|
||||
* Copyright (c) 2005, 2006, 2007 Antti Kantee. All Rights Reserved.
|
||||
*
|
||||
* Development of this software was supported by the
|
||||
* Google Summer of Code program and the Ulla Tuominen Foundation.
|
||||
@ -33,9 +33,10 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: puffs_msgif.c,v 1.15 2007/01/19 13:01:15 pooka Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: puffs_msgif.c,v 1.16 2007/01/26 22:59:49 pooka Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/fstrans.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/vnode.h>
|
||||
@ -80,6 +81,28 @@ puffs_vfstouser(struct puffs_mount *pmp, int optype, void *kbuf, size_t buflen)
|
||||
return touser(pmp, &park, puffs_getreqid(pmp), NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
puffs_suspendtouser(struct puffs_mount *pmp, int status)
|
||||
{
|
||||
struct puffs_vfsreq_suspend *pvfsr_susp;
|
||||
struct puffs_park *ppark;
|
||||
|
||||
pvfsr_susp = malloc(sizeof(struct puffs_vfsreq_suspend),
|
||||
M_PUFFS, M_WAITOK | M_ZERO);
|
||||
ppark = malloc(sizeof(struct puffs_park), M_PUFFS, M_WAITOK | M_ZERO);
|
||||
|
||||
pvfsr_susp->pvfsr_status = status;
|
||||
ppark->park_preq = (struct puffs_req *)pvfsr_susp;
|
||||
|
||||
ppark->park_preq->preq_opclass = PUFFSOP_VFS | PUFFSOPFLAG_FAF;
|
||||
ppark->park_preq->preq_optype = PUFFS_VFS_SUSPEND;
|
||||
|
||||
ppark->park_maxlen = ppark->park_copylen
|
||||
= sizeof(struct puffs_vfsreq_suspend);
|
||||
|
||||
(void)touser(pmp, ppark, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* vnode level request
|
||||
*/
|
||||
@ -193,9 +216,34 @@ static int
|
||||
touser(struct puffs_mount *pmp, struct puffs_park *ppark, uint64_t reqid,
|
||||
struct vnode *vp1, struct vnode *vp2)
|
||||
{
|
||||
struct mount *mp;
|
||||
struct puffs_req *preq;
|
||||
|
||||
mp = PMPTOMP(pmp);
|
||||
|
||||
/*
|
||||
* test for suspension lock.
|
||||
*
|
||||
* Note that we *DO NOT* keep the lock, since that might block
|
||||
* lock acquiring PLUS it would give userlandia control over
|
||||
* the lock. The operation queue enforces a strict ordering:
|
||||
* when the fs server gets in the op stream, it knows things
|
||||
* are in order. The kernel locks can't guarantee that for
|
||||
* userspace, in any case.
|
||||
*
|
||||
* BUT: this presents a problem for ops which have a consistency
|
||||
* clause based on more than one operation. Unfortunately such
|
||||
* operations (read, write) do not reliably work yet.
|
||||
*
|
||||
* Ya, Ya, it's wrong wong wrong, me be fixink this someday.
|
||||
*/
|
||||
if (fstrans_is_owner(mp))
|
||||
fstrans_start(mp, fstrans_lazy);
|
||||
else
|
||||
fstrans_start(mp, fstrans_normal);
|
||||
simple_lock(&pmp->pmp_lock);
|
||||
fstrans_done(mp);
|
||||
|
||||
if (pmp->pmp_status != PUFFSTAT_RUNNING) {
|
||||
simple_unlock(&pmp->pmp_lock);
|
||||
return ENXIO;
|
||||
@ -239,9 +287,24 @@ touser(struct puffs_mount *pmp, struct puffs_park *ppark, uint64_t reqid,
|
||||
wakeup(&pmp->pmp_req_touser);
|
||||
selnotify(pmp->pmp_sel, 0);
|
||||
|
||||
if (PUFFSOP_WANTREPLY(ppark->park_preq->preq_opclass))
|
||||
if (PUFFSOP_WANTREPLY(ppark->park_preq->preq_opclass)) {
|
||||
ltsleep(ppark, PUSER, "puffs1", 0, NULL);
|
||||
|
||||
/*
|
||||
* retake the lock and release. This makes sure (haha,
|
||||
* I'm humorous) that we don't process the same vnode in
|
||||
* multiple threads due to the locks hacks we have in
|
||||
* puffs_lock(). In reality this is well protected by
|
||||
* the biglock, but once that's gone, well, hopefully
|
||||
* this will be fixed for real. (and when you read this
|
||||
* comment in 2017 and subsequently barf, my condolences ;).
|
||||
*/
|
||||
if (!fstrans_is_owner(mp)) {
|
||||
fstrans_start(mp, fstrans_normal);
|
||||
fstrans_done(mp);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* relock */
|
||||
if (vp1)
|
||||
@ -250,6 +313,11 @@ touser(struct puffs_mount *pmp, struct puffs_park *ppark, uint64_t reqid,
|
||||
KASSERT(vn_lock(vp2, LK_EXCLUSIVE | LK_RETRY) == 0);
|
||||
#endif
|
||||
|
||||
simple_lock(&pmp->pmp_lock);
|
||||
if (--pmp->pmp_req_touser_waiters == 0)
|
||||
wakeup(&pmp->pmp_req_touser_waiters);
|
||||
simple_unlock(&pmp->pmp_lock);
|
||||
|
||||
return ppark->park_preq->preq_rv;
|
||||
}
|
||||
|
||||
@ -335,8 +403,6 @@ puffs_getop(struct puffs_mount *pmp, struct puffs_reqh_get *phg, int nonblock)
|
||||
donesome++;
|
||||
|
||||
simple_lock(&pmp->pmp_lock);
|
||||
pmp->pmp_req_touser_waiters--;
|
||||
|
||||
if (PUFFSOP_WANTREPLY(preq->preq_opclass)) {
|
||||
TAILQ_INSERT_TAIL(&pmp->pmp_req_replywait, park,
|
||||
park_entries);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: puffs_msgif.h,v 1.18 2007/01/16 22:38:19 pooka Exp $ */
|
||||
/* $NetBSD: puffs_msgif.h,v 1.19 2007/01/26 22:59:49 pooka Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005, 2006 Antti Kantee. All Rights Reserved.
|
||||
@ -59,7 +59,7 @@ enum {
|
||||
PUFFS_VFS_ROOT, PUFFS_VFS_STATVFS, PUFFS_VFS_SYNC,
|
||||
PUFFS_VFS_VGET, PUFFS_VFS_FHTOVP, PUFFS_VFS_VPTOFH,
|
||||
PUFFS_VFS_INIT, PUFFS_VFS_DONE, PUFFS_VFS_SNAPSHOT,
|
||||
PUFFS_VFS_EXTATTCTL
|
||||
PUFFS_VFS_EXTATTCTL, PUFFS_VFS_SUSPEND
|
||||
};
|
||||
#define PUFFS_VFS_MAX PUFFS_VFS_EXTATTCTL
|
||||
|
||||
@ -85,7 +85,7 @@ enum {
|
||||
#define PUFFS_VN_MAX PUFFS_VN_SETEXTATTR
|
||||
|
||||
#define PUFFSDEVELVERS 0x80000000
|
||||
#define PUFFSVERSION 2
|
||||
#define PUFFSVERSION 3
|
||||
#define PUFFSNAMESIZE 32
|
||||
struct puffs_args {
|
||||
unsigned int pa_vers;
|
||||
@ -270,6 +270,7 @@ struct puffs_flush {
|
||||
#if 0
|
||||
#define PUFFSFLUSHMULTIOP _IOW ('p', 6, struct puffs_flushmulti)
|
||||
#endif
|
||||
#define PUFFSSUSPENDOP _IO ('p', 7)
|
||||
|
||||
|
||||
/*
|
||||
@ -365,6 +366,16 @@ struct puffs_vfsreq_sync {
|
||||
int pvfsr_waitfor;
|
||||
};
|
||||
|
||||
struct puffs_vfsreq_suspend {
|
||||
struct puffs_req pvfsr_pr;
|
||||
|
||||
int pvfsr_status;
|
||||
};
|
||||
#define PUFFS_SUSPEND_START 0
|
||||
#define PUFFS_SUSPEND_SUSPENDED 1
|
||||
#define PUFFS_SUSPEND_RESUME 2
|
||||
#define PUFFS_SUSPEND_ERROR 3
|
||||
|
||||
/*
|
||||
* aux structures for vnode operations.
|
||||
*/
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: puffs_subr.c,v 1.17 2007/01/25 17:43:56 pooka Exp $ */
|
||||
/* $NetBSD: puffs_subr.c,v 1.18 2007/01/26 22:59:49 pooka Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005, 2006 Antti Kantee. All Rights Reserved.
|
||||
@ -33,7 +33,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: puffs_subr.c,v 1.17 2007/01/25 17:43:56 pooka Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: puffs_subr.c,v 1.18 2007/01/26 22:59:49 pooka Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/conf.h>
|
||||
@ -446,14 +446,14 @@ puffs_updatevpsize(struct vnode *vp)
|
||||
* We're dead, kaput, RIP, slightly more than merely pining for the
|
||||
* fjords, belly-up, fallen, lifeless, finished, expired, gone to meet
|
||||
* our maker, ceased to be, etcetc. YASD. It's a dead FS!
|
||||
*
|
||||
* Caller must hold puffs spinlock.
|
||||
*/
|
||||
void
|
||||
puffs_userdead(struct puffs_mount *pmp)
|
||||
{
|
||||
struct puffs_park *park;
|
||||
|
||||
simple_lock(&pmp->pmp_lock);
|
||||
|
||||
/*
|
||||
* Mark filesystem status as dying so that operations don't
|
||||
* attempt to march to userspace any longer.
|
||||
@ -473,6 +473,4 @@ puffs_userdead(struct puffs_mount *pmp)
|
||||
TAILQ_REMOVE(&pmp->pmp_req_touser, park, park_entries);
|
||||
wakeup(park);
|
||||
}
|
||||
|
||||
simple_unlock(&pmp->pmp_lock);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: puffs_sys.h,v 1.21 2007/01/21 16:29:31 pooka Exp $ */
|
||||
/* $NetBSD: puffs_sys.h,v 1.22 2007/01/26 22:59:49 pooka Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005, 2006 Antti Kantee. All Rights Reserved.
|
||||
@ -145,6 +145,7 @@ struct puffs_mount {
|
||||
uint64_t pmp_nextreq;
|
||||
uint8_t pmp_status;
|
||||
uint8_t pmp_unmounting;
|
||||
uint8_t pmp_suspend;
|
||||
};
|
||||
|
||||
#define PUFFSTAT_BEFOREINIT 0
|
||||
@ -152,7 +153,8 @@ struct puffs_mount {
|
||||
#define PUFFSTAT_RUNNING 2
|
||||
#define PUFFSTAT_DYING 3 /* Do you want your possessions identified? */
|
||||
|
||||
#define PNODE_NOREFS 0x01 /* vnode invalidated, no backend references */
|
||||
#define PNODE_NOREFS 0x01 /* vnode inactive, no backend reference */
|
||||
#define PNODE_SUSPEND 0x02 /* issue all operations as FAF */
|
||||
#if 0
|
||||
#define PNODE_LOCKED 0x0
|
||||
#define PNODE_WANTED 0x0
|
||||
@ -170,6 +172,7 @@ struct puffs_node {
|
||||
int puffs_start2(struct puffs_mount *, struct puffs_startreq *);
|
||||
|
||||
int puffs_vfstouser(struct puffs_mount *, int, void *, size_t);
|
||||
void puffs_suspendtouser(struct puffs_mount *, int);
|
||||
int puffs_vntouser(struct puffs_mount *, int, void *, size_t, void *,
|
||||
struct vnode *, struct vnode *);
|
||||
void puffs_vntouser_faf(struct puffs_mount *, int, void *, size_t, void *);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: puffs_transport.c,v 1.4 2007/01/09 18:14:31 pooka Exp $ */
|
||||
/* $NetBSD: puffs_transport.c,v 1.5 2007/01/26 22:59:49 pooka Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Antti Kantee. All Rights Reserved.
|
||||
@ -32,12 +32,14 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: puffs_transport.c,v 1.4 2007/01/09 18:14:31 pooka Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: puffs_transport.c,v 1.5 2007/01/26 22:59:49 pooka Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/filedesc.h>
|
||||
#include <sys/fstrans.h>
|
||||
#include <sys/kthread.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/poll.h>
|
||||
@ -200,6 +202,7 @@ puffs_fop_close(struct file *fp, struct lwp *l)
|
||||
*/
|
||||
mp = PMPTOMP(pmp);
|
||||
simple_unlock(&pi_lock);
|
||||
simple_lock(&pmp->pmp_lock);
|
||||
|
||||
/*
|
||||
* Free the waiting callers before proceeding any further.
|
||||
@ -223,7 +226,6 @@ puffs_fop_close(struct file *fp, struct lwp *l)
|
||||
* since pmp isn't locked. We might end up with PMP_DEAD after
|
||||
* restart and exit from there.
|
||||
*/
|
||||
simple_lock(&pmp->pmp_lock);
|
||||
if (pmp->pmp_unmounting) {
|
||||
ltsleep(&pmp->pmp_unmounting, PNORELOCK | PVFS, "puffsum",
|
||||
0, &pmp->pmp_lock);
|
||||
@ -233,6 +235,27 @@ puffs_fop_close(struct file *fp, struct lwp *l)
|
||||
}
|
||||
simple_unlock(&pmp->pmp_lock);
|
||||
|
||||
/*
|
||||
* Check that suspend isn't running. Issues here:
|
||||
* + we cannot nuke the mountpoint while suspend is running
|
||||
* because we risk nuking it from under us (as usual... does
|
||||
* anyone see a pattern forming?)
|
||||
* + we must have userdead or the suspend thread might deadlock.
|
||||
* this has been done above
|
||||
* + this DOES NOT solve the problem with the regular unmount path.
|
||||
* however, it is way way way way less likely a problem.
|
||||
* perhaps vfs_busy() in vfs_suspend() would help?
|
||||
*/
|
||||
simple_lock(&pmp->pmp_lock);
|
||||
if (pmp->pmp_suspend) {
|
||||
ltsleep(&pmp->pmp_suspend, PNORELOCK | PVFS, "puffsusum",
|
||||
0, &pmp->pmp_lock);
|
||||
DPRINTF(("puffs_fop_close: suspend was in progress for pmp %p, "
|
||||
"restart\n", pmp));
|
||||
goto restart;
|
||||
}
|
||||
simple_unlock(&pmp->pmp_lock);
|
||||
|
||||
/*
|
||||
* Detach from VFS. First do necessary XXX-dance (from
|
||||
* sys_unmount() & other callers of dounmount()
|
||||
@ -350,12 +373,51 @@ puffs_flush(struct puffs_mount *pmp, struct puffs_flush *pf)
|
||||
return rv;
|
||||
}
|
||||
|
||||
#ifdef NEWVNGATE
|
||||
static void
|
||||
dosuspendresume(void *arg)
|
||||
{
|
||||
struct puffs_mount *pmp = arg;
|
||||
struct mount *mp;
|
||||
int rv;
|
||||
|
||||
mp = PMPTOMP(pmp);
|
||||
/*
|
||||
* XXX? does this really do any good or is it just
|
||||
* paranoid stupidity? or stupid paranoia?
|
||||
*/
|
||||
if (mp->mnt_iflag & IMNT_UNMOUNT) {
|
||||
printf("puffs dosuspendresume(): detected suspend on "
|
||||
"unmounting fs\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* do the dance */
|
||||
rv = vfs_suspend(PMPTOMP(pmp), 0);
|
||||
if (rv == 0)
|
||||
vfs_resume(PMPTOMP(pmp));
|
||||
|
||||
simple_lock(&pmp->pmp_lock);
|
||||
KASSERT(pmp->pmp_suspend);
|
||||
pmp->pmp_suspend = 0;
|
||||
wakeup(&pmp->pmp_suspend);
|
||||
simple_unlock(&pmp->pmp_lock);
|
||||
|
||||
out:
|
||||
kthread_exit(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
puffs_fop_ioctl(struct file *fp, u_long cmd, void *data, struct lwp *l)
|
||||
{
|
||||
struct puffs_mount *pmp = FPTOPMP(fp);
|
||||
struct puffs_mount *pmp;
|
||||
int rv;
|
||||
|
||||
simple_lock(&pi_lock);
|
||||
pmp = FPTOPMP(fp);
|
||||
simple_unlock(&pi_lock);
|
||||
|
||||
if (pmp == PMP_EMBRYO || pmp == PMP_DEAD) {
|
||||
printf("puffs_fop_ioctl: puffs %p, not mounted\n", pmp);
|
||||
return ENOENT;
|
||||
@ -384,6 +446,23 @@ puffs_fop_ioctl(struct file *fp, u_long cmd, void *data, struct lwp *l)
|
||||
rv = puffs_flush(pmp, data);
|
||||
break;
|
||||
|
||||
case PUFFSSUSPENDOP:
|
||||
#ifdef NEWVNGATE
|
||||
rv = 0;
|
||||
simple_lock(&pmp->pmp_lock);
|
||||
if (pmp->pmp_suspend || pmp->pmp_status != PUFFSTAT_RUNNING)
|
||||
rv = EBUSY;
|
||||
else
|
||||
pmp->pmp_suspend = 1;
|
||||
simple_unlock(&pmp->pmp_lock);
|
||||
if (rv)
|
||||
break;
|
||||
rv = kthread_create1(dosuspendresume, pmp, NULL, "puffsusp");
|
||||
#else
|
||||
rv = EOPNOTSUPP;
|
||||
#endif
|
||||
break;
|
||||
|
||||
/* already done in sys_ioctl() */
|
||||
case FIONBIO:
|
||||
rv = 0;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: puffs_vfsops.c,v 1.25 2007/01/25 17:43:56 pooka Exp $ */
|
||||
/* $NetBSD: puffs_vfsops.c,v 1.26 2007/01/26 22:59:49 pooka Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005, 2006 Antti Kantee. All Rights Reserved.
|
||||
@ -33,7 +33,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: puffs_vfsops.c,v 1.25 2007/01/25 17:43:56 pooka Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: puffs_vfsops.c,v 1.26 2007/01/26 22:59:49 pooka Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/mount.h>
|
||||
@ -43,6 +43,7 @@ __KERNEL_RCSID(0, "$NetBSD: puffs_vfsops.c,v 1.25 2007/01/25 17:43:56 pooka Exp
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/dirent.h>
|
||||
#include <sys/kauth.h>
|
||||
#include <sys/fstrans.h>
|
||||
|
||||
#include <lib/libkern/libkern.h>
|
||||
|
||||
@ -130,6 +131,9 @@ puffs_mount(struct mount *mp, const char *path, void *data,
|
||||
mp->mnt_dev_bshift = DEV_BSHIFT;
|
||||
mp->mnt_flag &= ~MNT_LOCAL; /* we don't really know, so ... */
|
||||
mp->mnt_data = pmp;
|
||||
#ifdef NEWVNGATE
|
||||
mp->mnt_iflag |= IMNT_HAS_TRANS;
|
||||
#endif
|
||||
|
||||
pmp->pmp_status = PUFFSTAT_MOUNTING;
|
||||
pmp->pmp_nextreq = 0;
|
||||
@ -284,9 +288,24 @@ puffs_unmount(struct mount *mp, int mntflags, struct lwp *l)
|
||||
* screw what userland thinks and just die.
|
||||
*/
|
||||
if (error == 0 || force) {
|
||||
pmp->pmp_status = PUFFSTAT_DYING;
|
||||
/* tell waiters & other resources to go unwait themselves */
|
||||
puffs_userdead(pmp);
|
||||
puffs_nukebypmp(pmp);
|
||||
|
||||
/*
|
||||
* Sink waiters. This is still not perfect, since the
|
||||
* draining is done after userret, not when they really
|
||||
* exit the file system. It will probably work as almost
|
||||
* no call will block and therefore cause a context switch
|
||||
* and therefore will protected by the biglock after
|
||||
* exiting userspace. But ... it's an imperfect world.
|
||||
*/
|
||||
while (pmp->pmp_req_touser_waiters != 0)
|
||||
ltsleep(&pmp->pmp_req_touser_waiters, PVFS,
|
||||
"puffsink", 0, &pmp->pmp_lock);
|
||||
simple_unlock(&pmp->pmp_lock);
|
||||
|
||||
/* free resources now that we hopefully have no waiters left */
|
||||
free(pmp->pmp_pnodehash, M_PUFFS);
|
||||
FREE(pmp, M_PUFFS);
|
||||
error = 0;
|
||||
@ -415,15 +434,17 @@ puffs_statvfs(struct mount *mp, struct statvfs *sbp, struct lwp *l)
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
puffs_sync(struct mount *mp, int waitfor, struct kauth_cred *cred,
|
||||
struct lwp *l)
|
||||
static int
|
||||
pageflush(struct mount *mp, int waitfor, int suspending)
|
||||
{
|
||||
struct puffs_node *pn;
|
||||
struct vnode *vp, *nvp;
|
||||
int error, rv;
|
||||
int ppflags;
|
||||
int error, rv, ppflags;
|
||||
|
||||
PUFFS_VFSREQ(sync);
|
||||
KASSERT(((waitfor == MNT_WAIT) && suspending) == 0);
|
||||
KASSERT((suspending == 0)
|
||||
|| (fstrans_is_owner(mp)
|
||||
&& fstrans_getstate(mp) == fstrans_suspending));
|
||||
|
||||
error = 0;
|
||||
ppflags = PGO_CLEANIT | PGO_ALLPAGES;
|
||||
@ -444,6 +465,7 @@ puffs_sync(struct mount *mp, int waitfor, struct kauth_cred *cred,
|
||||
goto loop;
|
||||
|
||||
simple_lock(&vp->v_interlock);
|
||||
pn = VPTOPP(vp);
|
||||
nvp = TAILQ_NEXT(vp, v_mntvnodes);
|
||||
|
||||
if (vp->v_type != VREG || UVM_OBJ_IS_CLEAN(&vp->v_uobj)) {
|
||||
@ -467,6 +489,9 @@ puffs_sync(struct mount *mp, int waitfor, struct kauth_cred *cred,
|
||||
* dounmount(), when we are wait-flushing all the dirty
|
||||
* vnodes through other routes in any case. So there,
|
||||
* sync() doesn't actually sync. Happy now?
|
||||
*
|
||||
* NOTE: if we're suspending, vget() does NOT lock.
|
||||
* See puffs_lock() for details.
|
||||
*/
|
||||
rv = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK);
|
||||
if (rv) {
|
||||
@ -476,8 +501,36 @@ puffs_sync(struct mount *mp, int waitfor, struct kauth_cred *cred,
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Thread information to puffs_strategy() through the
|
||||
* pnode flags: we want to issue the putpages operations
|
||||
* as FAF if we're suspending, since it's very probable
|
||||
* that our execution context is that of the userspace
|
||||
* daemon. We can do this because:
|
||||
* + we send the "going to suspend" prior to this part
|
||||
* + if any of the writes fails in userspace, it's the
|
||||
* file system server's problem to decide if this was a
|
||||
* failed snapshot when it gets the "snapshot complete"
|
||||
* notification.
|
||||
* + if any of the writes fail in the kernel already, we
|
||||
* immediately fail *and* notify the user server of
|
||||
* failure.
|
||||
*
|
||||
* We also do FAFs if we're called from the syncer. This
|
||||
* is just general optimization for trickle sync: no need
|
||||
* to really guarantee that the stuff ended on backing
|
||||
* storage.
|
||||
* TODO: Maybe also hint the user server of this twist?
|
||||
*/
|
||||
simple_lock(&vp->v_interlock);
|
||||
if (suspending || waitfor == MNT_LAZY)
|
||||
pn->pn_stat |= PNODE_SUSPEND;
|
||||
rv = VOP_PUTPAGES(vp, 0, 0, ppflags);
|
||||
if (suspending || waitfor == MNT_LAZY) {
|
||||
simple_lock(&vp->v_interlock);
|
||||
pn->pn_stat &= ~PNODE_SUSPEND;
|
||||
simple_unlock(&vp->v_interlock);
|
||||
}
|
||||
if (rv)
|
||||
error = rv;
|
||||
vput(vp);
|
||||
@ -485,6 +538,19 @@ puffs_sync(struct mount *mp, int waitfor, struct kauth_cred *cred,
|
||||
}
|
||||
simple_unlock(&mntvnode_slock);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
puffs_sync(struct mount *mp, int waitfor, struct kauth_cred *cred,
|
||||
struct lwp *l)
|
||||
{
|
||||
int error, rv;
|
||||
|
||||
PUFFS_VFSREQ(sync);
|
||||
|
||||
error = pageflush(mp, waitfor, 0);
|
||||
|
||||
/* sync fs */
|
||||
sync_arg.pvfsr_waitfor = waitfor;
|
||||
puffs_credcvt(&sync_arg.pvfsr_cred, cred);
|
||||
@ -555,6 +621,50 @@ puffs_snapshot(struct mount *mp, struct vnode *vp, struct timespec *ts)
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int
|
||||
puffs_suspendctl(struct mount *mp, int cmd)
|
||||
{
|
||||
struct puffs_mount *pmp;
|
||||
int error;
|
||||
|
||||
pmp = MPTOPUFFSMP(mp);
|
||||
switch (cmd) {
|
||||
case SUSPEND_SUSPEND:
|
||||
DPRINTF(("puffs_suspendctl: suspending\n"));
|
||||
if ((error = fstrans_setstate(mp, fstrans_suspending)) != 0)
|
||||
break;
|
||||
puffs_suspendtouser(pmp, PUFFS_SUSPEND_START);
|
||||
|
||||
error = pageflush(mp, 0, 1);
|
||||
if (error == 0)
|
||||
error = fstrans_setstate(mp, fstrans_suspended);
|
||||
|
||||
if (error != 0) {
|
||||
puffs_suspendtouser(pmp, PUFFS_SUSPEND_ERROR);
|
||||
(void) fstrans_setstate(mp, fstrans_normal);
|
||||
break;
|
||||
}
|
||||
|
||||
puffs_suspendtouser(pmp, PUFFS_SUSPEND_SUSPENDED);
|
||||
|
||||
break;
|
||||
|
||||
case SUSPEND_RESUME:
|
||||
DPRINTF(("puffs_suspendctl: resume\n"));
|
||||
error = 0;
|
||||
(void) fstrans_setstate(mp, fstrans_normal);
|
||||
puffs_suspendtouser(pmp, PUFFS_SUSPEND_RESUME);
|
||||
break;
|
||||
|
||||
default:
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
DPRINTF(("puffs_suspendctl: return %d\n", error));
|
||||
return error;
|
||||
}
|
||||
|
||||
const struct vnodeopv_desc * const puffs_vnodeopv_descs[] = {
|
||||
&puffs_vnodeop_opv_desc,
|
||||
&puffs_specop_opv_desc,
|
||||
@ -581,7 +691,7 @@ struct vfsops puffs_vfsops = {
|
||||
NULL, /* mountroot */
|
||||
puffs_snapshot, /* snapshot */
|
||||
vfs_stdextattrctl, /* extattrctl */
|
||||
vfs_stdsuspendctl, /* suspendctl */
|
||||
puffs_suspendctl, /* suspendctl */
|
||||
puffs_vnodeopv_descs, /* vnodeops */
|
||||
0, /* refcount */
|
||||
{ NULL, NULL }
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: puffs_vnops.c,v 1.40 2007/01/25 23:43:57 pooka Exp $ */
|
||||
/* $NetBSD: puffs_vnops.c,v 1.41 2007/01/26 22:59:49 pooka Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005, 2006 Antti Kantee. All Rights Reserved.
|
||||
@ -33,13 +33,14 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: puffs_vnops.c,v 1.40 2007/01/25 23:43:57 pooka Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: puffs_vnops.c,v 1.41 2007/01/26 22:59:49 pooka Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/fstrans.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/vnode.h>
|
||||
|
||||
#include <fs/puffs/puffs_msgif.h>
|
||||
#include <fs/puffs/puffs_sys.h>
|
||||
@ -948,7 +949,7 @@ puffs_fsync(void *v)
|
||||
if (!EXISTSOP(pmp, FSYNC) || (pn->pn_stat & PNODE_NOREFS))
|
||||
return 0;
|
||||
|
||||
dofaf = (ap->a_flags & FSYNC_WAIT) == 0;
|
||||
dofaf = (ap->a_flags & FSYNC_WAIT) == 0 || ap->a_flags == FSYNC_LAZY;
|
||||
/*
|
||||
* We abuse VXLOCK to mean "vnode is going to die", so we issue
|
||||
* only FAFs for those. Otherwise there's a danger of deadlock,
|
||||
@ -1722,7 +1723,10 @@ puffs_strategy(void *v)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* See explanation for the necessity of a FAF in puffs_fsync
|
||||
* See explanation for the necessity of a FAF in puffs_fsync.
|
||||
*
|
||||
* Also, do FAF in case we're suspending.
|
||||
* See puffs_vfsops.c:pageflush()
|
||||
*
|
||||
* XXgoddamnX: B_WRITE is a "pseudo flag"
|
||||
*/
|
||||
@ -1730,6 +1734,8 @@ puffs_strategy(void *v)
|
||||
simple_lock(&vp->v_interlock);
|
||||
if (vp->v_flag & VXLOCK)
|
||||
dowritefaf = 1;
|
||||
if (pn->pn_stat & PNODE_SUSPEND)
|
||||
dowritefaf = 1;
|
||||
simple_unlock(&vp->v_interlock);
|
||||
}
|
||||
|
||||
@ -1893,9 +1899,7 @@ puffs_bmap(void *v)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* moreXXX: yes, todo
|
||||
*/
|
||||
|
||||
int
|
||||
puffs_lock(void *v)
|
||||
{
|
||||
@ -1904,11 +1908,29 @@ puffs_lock(void *v)
|
||||
int a_flags;
|
||||
} */ *ap = v;
|
||||
struct vnode *vp = ap->a_vp;
|
||||
struct mount *mp = vp->v_mount;
|
||||
|
||||
#if 0
|
||||
DPRINTF(("puffs_lock: lock %p, args 0x%x\n", vp, ap->a_flags));
|
||||
#endif
|
||||
|
||||
/*
|
||||
* XXX: this avoids deadlocking when we're suspending.
|
||||
* e.g. some ops holding the vnode lock might be blocked for
|
||||
* the vfs transaction lock so we'd deadlock.
|
||||
*
|
||||
* Now once again this is skating on the thin ice of modern life,
|
||||
* since we are breaking the consistency guarantee provided
|
||||
* _to the user server_ by vnode locking. Hopefully this will
|
||||
* get fixed soon enough by getting rid of the dependency on
|
||||
* vnode locks alltogether.
|
||||
*/
|
||||
if (fstrans_is_owner(mp) && fstrans_getstate(mp) == fstrans_suspending){
|
||||
if (ap->a_flags & LK_INTERLOCK)
|
||||
simple_unlock(&vp->v_interlock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return lockmgr(&vp->v_lock, ap->a_flags, &vp->v_interlock);
|
||||
}
|
||||
|
||||
@ -1920,11 +1942,19 @@ puffs_unlock(void *v)
|
||||
int a_flags;
|
||||
} */ *ap = v;
|
||||
struct vnode *vp = ap->a_vp;
|
||||
struct mount *mp = vp->v_mount;
|
||||
|
||||
#if 0
|
||||
DPRINTF(("puffs_unlock: lock %p, args 0x%x\n", vp, ap->a_flags));
|
||||
#endif
|
||||
|
||||
/* XXX: see puffs_lock() */
|
||||
if (fstrans_is_owner(mp) && fstrans_getstate(mp) == fstrans_suspending){
|
||||
if (ap->a_flags & LK_INTERLOCK)
|
||||
simple_unlock(&vp->v_interlock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return lockmgr(&vp->v_lock, ap->a_flags | LK_RELEASE, &vp->v_interlock);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user