- Postpone file close at reclaim time, since NetBSD sends fsync and

setattr(mtime, ctime) after close, while FUSE expects the file
to be open for these operations

- remove unused argument to node_mk_common()

- remove requeued requests when they are executed, not when they
are tagged for schedule

- try to make filehandle management simplier, by keeping track of only
one read and one write filehandle (the latter being really read/write).

- when CREATE is not available, we use the MKNOD/OPEN path. Fix a
bug here where we opened the parent directory instead of the node:
add the missing lookup of the mknod'ed node.

- lookup file we just created: glusterfs does not really see them
otherwise.

- open file when doing setattr(mtime, ctime) on non open files, as
some filesystems seems to require it.

- Do not flush pagecache for removed nodes

- Keep track of read/write operations in progress, and at reclaim
time, make sure they are over before closing and forgeting the file.
This commit is contained in:
manu 2010-09-03 07:15:18 +00:00
parent b5ec94d2d1
commit 28d5b6408e
3 changed files with 347 additions and 211 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: ops.c,v 1.6 2010/09/02 08:58:06 manu Exp $ */
/* $NetBSD: ops.c,v 1.7 2010/09/03 07:15:18 manu Exp $ */
/*-
* Copyright (c) 2010 Emmanuel Dreyfus. All rights reserved.
@ -41,6 +41,7 @@
#include "perfuse_priv.h"
#include "fuse.h"
static int node_close_common(struct puffs_usermount *, puffs_cookie_t, int);
static int no_access(puffs_cookie_t, const struct puffs_cred *, mode_t);
static void fuse_attr_to_vap(struct perfuse_state *,
struct vattr *, struct fuse_attr *);
@ -74,6 +75,7 @@ static void dequeue_requests(struct perfuse_state *,
*/
#define F_WAIT 0x010
#define F_FLOCK 0x020
#define OFLAGS(fflags) ((fflags) - 1)
/*
* Borrowed from src/sys/kern/vfs_subr.c and src/sys/sys/vnode.h
@ -90,6 +92,74 @@ const int vttoif_tab[9] = {
#define IFTOVT(mode) (iftovt_tab[((mode) & S_IFMT) >> 12])
#define VTTOIF(indx) (vttoif_tab[(int)(indx)])
static int
node_close_common(pu, opc, mode)
struct puffs_usermount *pu;
puffs_cookie_t opc;
int mode;
{
struct perfuse_state *ps;
perfuse_msg_t *pm;
int op;
uint64_t fh;
struct fuse_release_in *fri;
struct perfuse_node_data *pnd;
struct puffs_node *pn;
int error;
ps = puffs_getspecific(pu);
pn = (struct puffs_node *)opc;
pnd = PERFUSE_NODE_DATA(pn);
if (puffs_pn_getvap(pn)->va_type == VDIR) {
op = FUSE_RELEASEDIR;
mode = FREAD;
} else {
op = FUSE_RELEASE;
}
/*
* Destroy the filehandle before sending the
* request to the FUSE filesystem, otherwise
* we may get a second close() while we wait
* for the reply, and we would end up closing
* the same fh twice instead of closng both.
*/
fh = perfuse_get_fh(opc, mode);
perfuse_destroy_fh(pn, fh);
/*
* release_flags may be set to FUSE_RELEASE_FLUSH
* to flush locks. lock_owner must be set in that case
*/
pm = ps->ps_new_msg(pu, opc, op, sizeof(*fri), NULL);
fri = GET_INPAYLOAD(ps, pm, fuse_release_in);
fri->fh = fh;
fri->flags = 0;
fri->release_flags = 0;
fri->lock_owner = pnd->pnd_lock_owner;
fri->flags = (fri->lock_owner != 0) ? FUSE_RELEASE_FLUSH : 0;
#ifdef PERFUSE_DEBUG
if (perfuse_diagflags & PDF_FH)
DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n",
__func__, (void *)opc, pnd->pnd_ino, fri->fh);
#endif
if ((error = XCHG_MSG(ps, pu, pm, NO_PAYLOAD_REPLY_LEN)) != 0)
goto out;
ps->ps_destroy_msg(pm);
error = 0;
out:
if (error != 0)
DERRX(EX_SOFTWARE, "%s: freed fh = 0x%"PRIx64" but filesystem "
"returned error = %d", __func__, fh, error);
return error;
}
static int
no_access(opc, pcr, mode)
@ -801,6 +871,19 @@ perfuse_node_lookup(pu, opc, pni, pcn)
struct puffs_node *pn;
int error;
/*
* Special case for ..
*/
if (PCNISDOTDOT(pcn)) {
pn = PERFUSE_NODE_DATA(opc)->pnd_parent;
PERFUSE_NODE_DATA(pn)->pnd_flags &= ~PND_RECLAIMED;
puffs_newinfo_setcookie(pni, pn);
puffs_newinfo_setvtype(pni, VDIR);
return 0;
}
/*
* XXX This is borrowed from librefuse,
* and __UNCONST is said to be fixed.
@ -866,6 +949,12 @@ perfuse_node_create(pu, opc, pni, pcn, vap)
if (error != 0)
return error;
error = node_lookup_common(pu, opc, (char*)PCNPATH(pcn), &pn);
if (error != 0)
return error;
opc = (puffs_cookie_t)pn;
error = perfuse_node_open(pu, opc, FREAD|FWRITE, pcn->pcn_cred);
if (error != 0)
return error;
@ -898,12 +987,26 @@ perfuse_node_create(pu, opc, pni, pcn, vap)
* so that we can reuse it later
*/
pn = perfuse_new_pn(pu, opc);
perfuse_new_fh((puffs_cookie_t)pn, foo->fh);
perfuse_new_fh((puffs_cookie_t)pn, foo->fh, FWRITE);
PERFUSE_NODE_DATA(pn)->pnd_ino = feo->nodeid;
#ifdef PERFUSE_DEBUG
if (perfuse_diagflags & PDF_FH)
DPRINTF("%s: opc = %p, file = \"%s\", "
"ino = %"PRId64", rfh = 0x%"PRIx64"\n",
__func__, (void *)pn, (char *)PCNPATH(pcn),
feo->nodeid, foo->fh);
#endif
fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr);
puffs_newinfo_setcookie(pni, pn);
/*
* It seems we need to do this so that glusterfs gets fully
* aware that the file was created. If we do not do it, we
* get "SETATTR (null) (fuse_loc_fill() failed)"
*/
(void)node_lookup_common(pu, opc, (char*)PCNPATH(pcn), NULL);
out:
ps->ps_destroy_msg(pm);
@ -979,8 +1082,10 @@ perfuse_node_open(pu, opc, mode, pcr)
const struct puffs_cred *pcr;
{
struct perfuse_state *ps;
struct perfuse_node_data *pnd;
perfuse_msg_t *pm;
mode_t pmode;
mode_t fmode;
int op;
struct fuse_open_in *foi;
struct fuse_open_out *foo;
@ -988,6 +1093,7 @@ perfuse_node_open(pu, opc, mode, pcr)
int error;
ps = puffs_getspecific(pu);
pnd = PERFUSE_NODE_DATA(opc);
pn = (struct puffs_node *)opc;
if (puffs_pn_getvap(pn)->va_type == VDIR) {
@ -995,8 +1101,8 @@ perfuse_node_open(pu, opc, mode, pcr)
pmode = PUFFS_VREAD|PUFFS_VEXEC;
} else {
op = FUSE_OPEN;
if (mode & (O_RDWR|O_WRONLY))
pmode = PUFFS_VWRITE;
if (mode & FWRITE)
pmode = PUFFS_VWRITE|PUFFS_VREAD;
else
pmode = PUFFS_VREAD;
}
@ -1006,8 +1112,7 @@ perfuse_node_open(pu, opc, mode, pcr)
* Opening a file requires R-- for reading, -W- for writing
* In both cases, --X is required on the parent.
*/
if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
pcr, PUFFS_VEXEC))
if (no_access((puffs_cookie_t)pnd->pnd_parent, pcr, PUFFS_VEXEC))
return EACCES;
if (no_access(opc, pcr, pmode))
@ -1017,23 +1122,30 @@ perfuse_node_open(pu, opc, mode, pcr)
* libfuse docs say O_CREAT should not be set.
*/
mode &= ~O_CREAT;
/*
* Do not open twice, and do not reopen for reading
* if we already have write handle.
* Directories are always open with read access only,
* whatever flags we get.
*/
if (op == FUSE_OPENDIR)
mode = (mode & ~(FREAD|FWRITE)) | FREAD;
if ((mode & FREAD) && (pnd->pnd_flags & PND_RFH))
return 0;
if ((mode & FWRITE) && (pnd->pnd_flags & PND_WFH))
return 0;
/*
* Convert PUFFS mode to FUSE mode: convert FREAD/FWRITE
* to O_RDONLY/O_WRONLY while perserving the other options.
*/
fmode = mode & ~(FREAD|FWRITE);
fmode |= (mode & FWRITE) ? O_RDWR : O_RDONLY;
pm = ps->ps_new_msg(pu, opc, op, sizeof(*foi), pcr);
foi = GET_INPAYLOAD(ps, pm, fuse_open_in);
foi->flags = mode & ~O_ACCMODE;
switch (mode & (FREAD|FWRITE)) {
case FREAD|FWRITE:
foi->flags |= O_RDWR;
break;
case FREAD:
foi->flags |= O_RDONLY;
break;
case FWRITE:
foi->flags |= O_WRONLY;
break;
default:
break;
}
foi->flags = fmode;
foi->unused = 0;
if ((error = XCHG_MSG(ps, pu, pm, sizeof(*foo))) != 0)
@ -1045,15 +1157,16 @@ perfuse_node_open(pu, opc, mode, pcr)
* Save the file handle in node private data
* so that we can reuse it later
*/
perfuse_new_fh((puffs_cookie_t)pn, foo->fh);
perfuse_new_fh((puffs_cookie_t)pn, foo->fh, mode);
#ifdef PERFUSE_DEBUG
if (perfuse_diagflags & PDF_FH)
DPRINTF("%s: opc = %p, file = \"%s\", "
"ino = %"PRId64", fh = 0x%"PRIx64"\n",
"ino = %"PRId64", %s%sfh = 0x%"PRIx64"\n",
__func__, (void *)opc,
(char *)PNPATH((struct puffs_node *)opc),
PERFUSE_NODE_DATA(opc)->pnd_ino, foo->fh);
pnd->pnd_ino, mode & FREAD ? "r" : "",
mode & FWRITE ? "w" : "", foo->fh);
#endif
out:
ps->ps_destroy_msg(pm);
@ -1061,7 +1174,7 @@ out:
return error;
}
/* ARGSUSED2 */
/* ARGSUSED0 */
int
perfuse_node_close(pu, opc, flags, pcr)
struct puffs_usermount *pu;
@ -1069,95 +1182,34 @@ perfuse_node_close(pu, opc, flags, pcr)
int flags;
const struct puffs_cred *pcr;
{
struct perfuse_state *ps;
perfuse_msg_t *pm;
int op;
uint64_t fh;
struct perfuse_node_data *pnd;
struct fuse_release_in *fri;
struct puffs_node *pn;
int error;
ps = puffs_getspecific(pu);
pn = (struct puffs_node *)opc;
pnd = PERFUSE_NODE_DATA(pn);
struct perfuse_node_data *pnd;
if (puffs_pn_getvap(pn)->va_type == VDIR)
op = FUSE_RELEASEDIR;
else
op = FUSE_RELEASE;
pn = (struct puffs_node *)opc;
pnd = PERFUSE_NODE_DATA(opc);
if (!(pnd->pnd_flags & PND_OPEN))
return EBADF;
fh = perfuse_get_fh(opc);
/*
* Sync before close for files
* Make sure all operation are finished
* There can be an ongoing write, or queued operations
* XXX perhaps deadlock. Use requeue_request
*/
if ((op == FUSE_RELEASE) && (pnd->pnd_flags & PND_DIRTY)) {
#ifdef PERFUSE_DEBUG
if (perfuse_diagflags & PDF_SYNC)
DPRINTF("%s: SYNC opc = %p, file = \"%s\"\n",
__func__, (void*)opc, (char *)PNPATH(pn));
#endif
if ((error = perfuse_node_fsync(pu, opc, pcr, 0, 0, 0)) != 0)
return error;
while ((pnd->pnd_flags & PND_BUSY) ||
!TAILQ_EMPTY(&pnd->pnd_pcq))
puffs_cc_yield(puffs_cc_getcc(pu));
pnd->pnd_flags &= ~PND_DIRTY;
#ifdef PERFUSE_DEBUG
if (perfuse_diagflags & PDF_SYNC)
DPRINTF("%s: CLEAR opc = %p, file = \"%s\"\n",
__func__, (void*)opc, (char *)PNPATH((pn)));
#endif
}
/*
* Destroy the filehandle before sending the
* request to the FUSE filesystem, otherwise
* we may get a second close() while we wait
* for the reply, and we would end up closing
* the same fh twice instead of closng both.
/*
* The NetBSD kernel will send sync and setattr(mtime, ctime)
* afer a close on a regular file. Some FUSE filesystem will
* assume theses operations are performed on open files. We
* therefore postpone the close operation at reclaim time.
*/
perfuse_destroy_fh(pn, fh);
if (puffs_pn_getvap(pn)->va_type != VREG)
return node_close_common(pu, opc, flags);
#ifdef PERFUSE_DEBUG
if (perfuse_diagflags & PDF_FH)
DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n",
__func__, (void *)opc, pnd->pnd_ino, fh);
#endif
/*
* release_flags may be set to FUSE_RELEASE_FLUSH
* to flush locks. lock_owner must be set in that case
*/
pm = ps->ps_new_msg(pu, opc, op, sizeof(*fri), NULL);
fri = GET_INPAYLOAD(ps, pm, fuse_release_in);
fri->fh = fh;
fri->flags = 0;
fri->release_flags = 0;
fri->lock_owner = PERFUSE_NODE_DATA(pn)->pnd_lock_owner;
fri->flags = (fri->lock_owner != 0) ? FUSE_RELEASE_FLUSH : 0;
#ifdef PERFUSE_DEBUG
if (perfuse_diagflags & PDF_FH)
DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n",
__func__, (void *)opc, pnd->pnd_ino, fri->fh);
#endif
if ((error = XCHG_MSG(ps, pu, pm, NO_PAYLOAD_REPLY_LEN)) != 0)
goto out;
out:
if (error != 0)
DWARNX("%s: freed fh = 0x%"PRIx64" but filesystem "
"returned error = %d",
__func__, fh, error);
ps->ps_destroy_msg(pm);
return error;
return 0;
}
int
@ -1200,7 +1252,7 @@ perfuse_node_access(pu, opc, mode, pcr)
fgi = GET_INPAYLOAD(ps, pm, fuse_getattr_in);
fgi->getattr_flags = 0;
fgi->dummy = 0;
fgi->fh = perfuse_get_fh(opc);
fgi->fh = perfuse_get_fh(opc, FREAD);
#ifdef PERFUSE_DEBUG
if (perfuse_diagflags & PDF_FH)
@ -1288,15 +1340,20 @@ perfuse_node_setattr(pu, opc, vap, pcr)
perfuse_msg_t *pm;
uint64_t fh;
struct perfuse_state *ps;
struct perfuse_node_data *pnd;
struct fuse_setattr_in *fsi;
int error;
int open_self;
struct vattr *old_va;
open_self = 0;
ps = puffs_getspecific(pu);
pnd = PERFUSE_NODE_DATA(opc);
/*
* setattr requires --X on the parent directory
*/
if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
pcr, PUFFS_VEXEC))
if (no_access((puffs_cookie_t)pnd->pnd_parent, pcr, PUFFS_VEXEC))
return EACCES;
old_va = puffs_pn_getvap((struct puffs_node *)opc);
@ -1331,21 +1388,38 @@ perfuse_node_setattr(pu, opc, vap, pcr)
*/
if ((vap->va_mode != (mode_t)PUFFS_VNOVAL) &&
(puffs_access_chmod(old_va->va_uid, old_va->va_gid,
old_va->va_type, vap->va_mode, pcr)) != 0)
old_va->va_type, vap->va_mode, pcr)) != 0)
return EACCES;
/*
* setattr(mtime, ctime) require an open file,
* at least for glusterfs.
*/
if (((vap->va_atime.tv_sec != (time_t)PUFFS_VNOVAL) ||
(vap->va_mtime.tv_sec != (time_t)PUFFS_VNOVAL)) &&
!(pnd->pnd_flags & PND_WFH)) {
if ((error = perfuse_node_open(pu, opc, FWRITE, pcr)) != 0)
return error;
open_self = 1;
}
/*
* It seems troublesome to resize a file while
* a write is just beeing done. Wait for
* it to finish.
*/
if (vap->va_size != (u_quad_t)PUFFS_VNOVAL)
while (pnd->pnd_flags & PND_INWRITE)
requeue_request(pu, opc, PCQ_AFTERWRITE);
ps = puffs_getspecific(pu);
pm = ps->ps_new_msg(pu, opc, FUSE_SETATTR, sizeof(*fsi), pcr);
fsi = GET_INPAYLOAD(ps, pm, fuse_setattr_in);
fsi->valid = 0;
if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_OPEN) {
fh = perfuse_get_fh(opc);
if (pnd->pnd_flags & PND_WFH) {
fh = perfuse_get_fh(opc, FWRITE);
fsi->fh = fh;
if (fh != FUSE_UNKNOWN_FH)
fsi->valid |= FUSE_FATTR_FH;
fsi->valid |= FUSE_FATTR_FH;
}
if (vap->va_size != (u_quad_t)PUFFS_VNOVAL) {
@ -1380,8 +1454,8 @@ perfuse_node_setattr(pu, opc, vap, pcr)
fsi->valid |= FUSE_FATTR_GID;
}
if (PERFUSE_NODE_DATA(opc)->pnd_lock_owner != 0) {
fsi->lock_owner = PERFUSE_NODE_DATA(opc)->pnd_lock_owner;
if (pnd->pnd_lock_owner != 0) {
fsi->lock_owner = pnd->pnd_lock_owner;
fsi->valid |= FUSE_FATTR_LOCKOWNER;
}
@ -1392,6 +1466,9 @@ perfuse_node_setattr(pu, opc, vap, pcr)
ps->ps_destroy_msg(pm);
if (open_self)
(void)perfuse_node_close(pu, opc, FWRITE, pcr);
return error;
}
@ -1413,7 +1490,7 @@ perfuse_node_poll(pu, opc, events)
*/
pm = ps->ps_new_msg(pu, opc, FUSE_POLL, sizeof(*fpi), NULL);
fpi = GET_INPAYLOAD(ps, pm, fuse_poll_in);
fpi->fh = perfuse_get_fh(opc);
fpi->fh = perfuse_get_fh(opc, FREAD);
fpi->kh = 0;
fpi->flags = 0;
@ -1479,7 +1556,7 @@ perfuse_node_fsync(pu, opc, pcr, flags, offlo, offhi)
/*
* Do not sync if there are no change to sync
* XXX remove that testif we implement mmap
* XXX remove that test if we implement mmap
*/
pnd = PERFUSE_NODE_DATA(opc);
#ifdef PERFUSE_DEBUG
@ -1498,12 +1575,12 @@ perfuse_node_fsync(pu, opc, pcr, flags, offlo, offhi)
* "FSYNC() ERR => -1 (Invalid argument)"
*/
if (!(pnd->pnd_flags & PND_OPEN)) {
if ((error = perfuse_node_open(pu, opc, FREAD, pcr)) != 0)
if ((error = perfuse_node_open(pu, opc, FWRITE, pcr)) != 0)
goto out;
open_self = 1;
}
fh = perfuse_get_fh(opc);
fh = perfuse_get_fh(opc, FWRITE);
/*
* If fsync_flags is set, meta data should not be flushed.
@ -1543,8 +1620,8 @@ out:
if (pm != NULL)
ps->ps_destroy_msg(pm);
if (open_self)
(void)perfuse_node_close(pu, opc, 0, pcr);
if (open_self)
(void)node_close_common(pu, opc, FWRITE);
return error;
}
@ -1576,26 +1653,28 @@ perfuse_node_remove(pu, opc, targ, pcn)
const struct puffs_cn *pcn;
{
struct perfuse_state *ps;
perfuse_msg_t *pm;
struct puffs_node *pn;
struct perfuse_node_data *pnd;
perfuse_msg_t *pm;
char *path;
const char *name;
size_t len;
int error;
pnd = PERFUSE_NODE_DATA(opc);
/*
* remove requires -WX on the parent directory
* no right required on the object.
*/
if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
if (no_access((puffs_cookie_t)pnd->pnd_parent,
pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
return EACCES;
ps = puffs_getspecific(pu);
if (targ == NULL)
DERRX(EX_SOFTWARE, "%s: targ is NULL", __func__);
ps = puffs_getspecific(pu);
pn = (struct puffs_node *)targ;
name = basename_r((char *)PNPATH(pn));
len = strlen(name) + 1;
@ -1610,9 +1689,6 @@ perfuse_node_remove(pu, opc, targ, pcn)
if (puffs_inval_namecache_dir(pu, opc) != 0)
DERR(EX_OSERR, "puffs_inval_namecache_dir failed");
if (puffs_inval_pagecache_node(pu, (puffs_cookie_t)pn) != 0)
DERR(EX_OSERR, "puffs_inval_namecache_node failed");
puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
/*
@ -1767,6 +1843,7 @@ perfuse_node_rmdir(pu, opc, targ, pcn)
const struct puffs_cn *pcn;
{
struct perfuse_state *ps;
struct perfuse_node_data *pnd;
perfuse_msg_t *pm;
struct puffs_node *pn;
char *path;
@ -1774,11 +1851,13 @@ perfuse_node_rmdir(pu, opc, targ, pcn)
size_t len;
int error;
pnd = PERFUSE_NODE_DATA(opc);
/*
* remove requires -WX on the parent directory
* no right required on the object.
*/
if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
if (no_access((puffs_cookie_t)pnd->pnd_parent,
pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
return EACCES;
@ -1797,9 +1876,6 @@ perfuse_node_rmdir(pu, opc, targ, pcn)
if (puffs_inval_namecache_dir(pu, opc) != 0)
DERR(EX_OSERR, "puffs_inval_namecache_dir failed");
if (puffs_inval_pagecache_node(pu, (puffs_cookie_t)pn) != 0)
DERR(EX_OSERR, "puffs_inval_namecache_node failed");
puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
out:
@ -1905,17 +1981,17 @@ perfuse_node_readdir(pu, opc, dent, readoff,
* It seems NetBSD can call readdir without open first
* libfuse will crash if it is done that way, hence open first.
*/
if (!(PERFUSE_NODE_DATA(opc)->pnd_flags & PND_OPEN)) {
if (!(pnd->pnd_flags & PND_OPEN)) {
if ((error = perfuse_node_open(pu, opc, FREAD, pcr)) != 0)
goto out;
open_self = 1;
}
fh = perfuse_get_fh(opc);
fh = perfuse_get_fh(opc, FREAD);
#ifdef PERFUSE_DEBUG
if (perfuse_diagflags & PDF_FH)
DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n",
DPRINTF("%s: opc = %p, ino = %"PRId64", rfh = 0x%"PRIx64"\n",
__func__, (void *)opc,
PERFUSE_NODE_DATA(opc)->pnd_ino, fh);
#endif
@ -2013,7 +2089,7 @@ out:
* errors are ignored.
*/
if (open_self)
(void)perfuse_node_close(pu, opc, 0, pcr);
(void)perfuse_node_close(pu, opc, FWRITE, pcr);
if (error == 0)
error = readdir_buffered(ps, opc, dent, readoff,
@ -2091,12 +2167,6 @@ perfuse_node_reclaim(pu, opc)
ps = puffs_getspecific(pu);
pnd = PERFUSE_NODE_DATA(opc);
/*
* Make sure open files are properly closed when reclaimed.
*/
while (pnd->pnd_flags & PND_OPEN)
(void)perfuse_node_close(pu, opc, 0, NULL);
/*
* Never forget the root.
*/
@ -2121,15 +2191,17 @@ perfuse_node_reclaim(pu, opc)
#ifdef PERFUSE_DEBUG
if (perfuse_diagflags & PDF_RECLAIM)
DPRINTF("%s (nodeid %"PRId64") is %sreclaimed, "
"has childcount %d, %sopen\n",
"has childcount %d %s%s%s, pending ops:%s%s%s%s\n",
(char *)PNPATH(pn), pnd->pnd_ino,
pnd->pnd_flags & PND_RECLAIMED ? "" : "not ",
pnd->pnd_childcount,
pnd->pnd_flags & PND_OPEN ? "" : "not ");
if (pnd->pnd_flags & PND_OPEN)
DWARNX("%s: (nodeid %"PRId64") %s is still open",
__func__, pnd->pnd_ino, (char *)PNPATH(pn));
pnd->pnd_flags & PND_OPEN ? "open " : "not open",
pnd->pnd_flags & PND_RFH ? "r" : "",
pnd->pnd_flags & PND_WFH ? "w" : "",
pnd->pnd_flags & PND_INREADDIR ? " readdir" : "",
pnd->pnd_flags & PND_INREAD ? " read" : "",
pnd->pnd_flags & PND_INWRITE ? " write" : "",
pnd->pnd_flags & PND_BUSY ? "" : " none");
#endif
if (!(pnd->pnd_flags & PND_RECLAIMED) ||
@ -2137,12 +2209,38 @@ perfuse_node_reclaim(pu, opc)
return 0;
/*
* If the file is still open, close all file handles
* XXX no pcr arguement to send.
* Make sure all operation are finished
* There can be an ongoing write, or queued operations
*/
while(pnd->pnd_flags & PND_OPEN)
(void)perfuse_node_close(pu, opc, 0, NULL);
while (pnd->pnd_flags & PND_INWRITE) {
requeue_request(pu, opc, PCQ_AFTERWRITE);
/*
* It may have been cancelled in the meantime
*/
if (!(pnd->pnd_flags & PND_RECLAIMED))
return 0;
}
#ifdef PERFUSE_DEBUG
if ((pnd->pnd_flags & PND_BUSY) ||
!TAILQ_EMPTY(&pnd->pnd_pcq))
DERRX(EX_SOFTWARE, "%s: opc = %p: ongoing operations",
__func__, (void *)opc);
#endif
/*
* Close open files
*/
if (pnd->pnd_flags & PND_WFH)
(void)node_close_common(pu, opc, FREAD);
if (pnd->pnd_flags & PND_RFH)
(void)node_close_common(pu, opc, FWRITE);
/*
* And send the FORGET message
*/
pm = ps->ps_new_msg(pu, (puffs_cookie_t)pn, FUSE_FORGET,
sizeof(*ffi), NULL);
ffi = GET_INPAYLOAD(ps, pm, fuse_forget_in);
@ -2223,7 +2321,7 @@ perfuse_node_advlock(pu, opc, id, op, fl, flags)
pm = ps->ps_new_msg(pu, opc, fop, sizeof(*fli), NULL);
fli = GET_INPAYLOAD(ps, pm, fuse_lk_in);
fli->fh = perfuse_get_fh(opc);
fli->fh = perfuse_get_fh(opc, FWRITE);
fli->owner = fl->l_pid;
fli->lk.start = fl->l_start;
fli->lk.end = fl->l_start + fl->l_len;
@ -2279,6 +2377,7 @@ perfuse_node_read(pu, opc, buf, offset, resid, pcr, ioflag)
int ioflag;
{
struct perfuse_state *ps;
struct perfuse_node_data *pnd;
perfuse_msg_t *pm;
struct fuse_read_in *fri;
struct fuse_out_header *foh;
@ -2287,8 +2386,14 @@ perfuse_node_read(pu, opc, buf, offset, resid, pcr, ioflag)
int error;
ps = puffs_getspecific(pu);
pnd = PERFUSE_NODE_DATA(opc);
pm = NULL;
if (puffs_pn_getvap((struct puffs_node *)opc)->va_type == VDIR)
return EBADF;
pnd->pnd_flags |= PND_INREAD;
requested = *resid;
if ((ps->ps_readahead + requested) > ps->ps_max_readahead) {
if (perfuse_diagflags & PDF_REQUEUE)
@ -2307,19 +2412,18 @@ perfuse_node_read(pu, opc, buf, offset, resid, pcr, ioflag)
*/
pm = ps->ps_new_msg(pu, opc, FUSE_READ, sizeof(*fri), pcr);
fri = GET_INPAYLOAD(ps, pm, fuse_read_in);
fri->fh = perfuse_get_fh(opc);
fri->fh = perfuse_get_fh(opc, FREAD);
fri->offset = offset;
fri->size = (uint32_t)MIN(*resid, PAGE_SIZE - sizeof(*foh));
fri->read_flags = 0; /* XXX Unused by libfuse? */
fri->lock_owner = PERFUSE_NODE_DATA(opc)->pnd_lock_owner;
fri->lock_owner = pnd->pnd_lock_owner;
fri->flags = 0;
fri->flags |= (fri->lock_owner != 0) ? FUSE_READ_LOCKOWNER : 0;
#ifdef PERFUSE_DEBUG
if (perfuse_diagflags & PDF_FH)
DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n",
__func__, (void *)opc,
PERFUSE_NODE_DATA(opc)->pnd_ino, fri->fh);
__func__, (void *)opc, pnd->pnd_ino, fri->fh);
#endif
error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN);
@ -2351,6 +2455,8 @@ out:
ps->ps_readahead -= requested;
dequeue_requests(ps, opc, PCQ_READ, 1);
pnd->pnd_flags &= ~PND_INREAD;
return error;
}
@ -2365,6 +2471,7 @@ perfuse_node_write(pu, opc, buf, offset, resid, pcr, ioflag)
int ioflag;
{
struct perfuse_state *ps;
struct perfuse_node_data *pnd;
perfuse_msg_t *pm;
struct fuse_write_in *fwi;
struct fuse_write_out *fwo;
@ -2375,9 +2482,16 @@ perfuse_node_write(pu, opc, buf, offset, resid, pcr, ioflag)
int error;
ps = puffs_getspecific(pu);
pnd = PERFUSE_NODE_DATA(opc);
pm = NULL;
written = 0;
if (puffs_pn_getvap((struct puffs_node *)opc)->va_type == VDIR)
return EBADF;
DPRINTF("%s ENTER\n", __func__);
pnd->pnd_flags |= PND_INWRITE;
requested = *resid;
if ((ps->ps_write + requested) > ps->ps_max_write) {
if (perfuse_diagflags & PDF_REQUEUE)
@ -2403,11 +2517,11 @@ perfuse_node_write(pu, opc, buf, offset, resid, pcr, ioflag)
*/
pm = ps->ps_new_msg(pu, opc, FUSE_WRITE, payload_len, pcr);
fwi = GET_INPAYLOAD(ps, pm, fuse_write_in);
fwi->fh = perfuse_get_fh(opc);
fwi->fh = perfuse_get_fh(opc, FWRITE);
fwi->offset = offset;
fwi->size = (uint32_t)data_len;
fwi->write_flags = (fwi->size % PAGE_SIZE) ? 0 : 1;
fwi->lock_owner = PERFUSE_NODE_DATA(opc)->pnd_lock_owner;
fwi->lock_owner = pnd->pnd_lock_owner;
fwi->flags = 0;
fwi->flags |= (fwi->lock_owner != 0) ? FUSE_WRITE_LOCKOWNER : 0;
fwi->flags |= (ioflag & IO_DIRECT) ? 0 : FUSE_WRITE_CACHE;
@ -2416,8 +2530,7 @@ perfuse_node_write(pu, opc, buf, offset, resid, pcr, ioflag)
#ifdef PERFUSE_DEBUG
if (perfuse_diagflags & PDF_FH)
DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n",
__func__, (void *)opc,
PERFUSE_NODE_DATA(opc)->pnd_ino, fwi->fh);
__func__, (void *)opc, pnd->pnd_ino, fwi->fh);
#endif
if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fwo))) != 0)
goto out;
@ -2447,7 +2560,7 @@ perfuse_node_write(pu, opc, buf, offset, resid, pcr, ioflag)
/*
* Remember to sync the file
*/
PERFUSE_NODE_DATA(opc)->pnd_flags |= PND_DIRTY;
pnd->pnd_flags |= PND_DIRTY;
#ifdef PERFUSE_DEBUG
if (perfuse_diagflags & PDF_SYNC)
@ -2462,6 +2575,13 @@ out:
ps->ps_write -= requested;
dequeue_requests(ps, opc, PCQ_WRITE, 1);
pnd->pnd_flags &= ~PND_INWRITE;
/*
* Dequeue operation that were waiting for write to complete
*/
dequeue_requests(ps, opc, PCQ_AFTERWRITE, DEQUEUE_ALL);
return error;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: perfuse_priv.h,v 1.4 2010/09/01 14:57:24 manu Exp $ */
/* $NetBSD: perfuse_priv.h,v 1.5 2010/09/03 07:15:18 manu Exp $ */
/*-
* Copyright (c) 2010 Emmanuel Dreyfus. All rights reserved.
@ -71,13 +71,7 @@ struct perfuse_state {
};
struct perfuse_file_handle {
uint64_t pfh_fh;
TAILQ_ENTRY(perfuse_file_handle) pfh_entries;
};
enum perfuse_qtype { PCQ_READDIR, PCQ_READ, PCQ_WRITE };
enum perfuse_qtype { PCQ_READDIR, PCQ_READ, PCQ_WRITE, PCQ_AFTERWRITE };
struct perfuse_cc_queue {
enum perfuse_qtype pcq_type;
@ -87,7 +81,8 @@ struct perfuse_cc_queue {
struct perfuse_node_data {
TAILQ_HEAD(,perfuse_file_handle) pnd_fh;
uint64_t pnd_rfh;
uint64_t pnd_wfh;
uint64_t pnd_ino; /* inode */
uint64_t pnd_nlookup; /* vnode refcount */
uint64_t pnd_offset; /* seek state */
@ -98,10 +93,16 @@ struct perfuse_node_data {
size_t pnd_all_fd_len;
TAILQ_HEAD(,perfuse_cc_queue) pnd_pcq; /* queued requests */
int pnd_flags;
#define PND_RECLAIMED 0x1 /* reclaim pending */
#define PND_INREADDIR 0x2 /* readdir in progress */
#define PND_OPEN 0x4 /* At least one fh is allocated */
#define PND_DIRTY 0x8 /* There is some data to sync */
#define PND_RECLAIMED 0x01 /* reclaim pending */
#define PND_INREADDIR 0x02 /* readdir in progress */
#define PND_DIRTY 0x04 /* There is some data to sync */
#define PND_RFH 0x08 /* Read FH allocated */
#define PND_WFH 0x10 /* Write FH allocated */
#define PND_INREAD 0x20 /* read in progress */
#define PND_INWRITE 0x40 /* write in progress */
#define PND_OPEN (PND_RFH|PND_WFH) /* File is open */
#define PND_BUSY (PND_INREADDIR|PND_INREAD|PND_INWRITE)
puffs_cookie_t pnd_parent;
int pnd_childcount;
};
@ -131,9 +132,9 @@ __BEGIN_DECLS
struct puffs_node *perfuse_new_pn(struct puffs_usermount *,
struct puffs_node *);
void perfuse_destroy_pn(struct puffs_node *);
void perfuse_new_fh(puffs_cookie_t, uint64_t);
void perfuse_new_fh(puffs_cookie_t, uint64_t, int);
void perfuse_destroy_fh(puffs_cookie_t, uint64_t);
uint64_t perfuse_get_fh(puffs_cookie_t);
uint64_t perfuse_get_fh(puffs_cookie_t, int);
uint64_t perfuse_next_unique(struct puffs_usermount *);
char *perfuse_fs_mount(int, ssize_t);

View File

@ -1,4 +1,4 @@
/* $NetBSD: subr.c,v 1.3 2010/09/01 14:57:24 manu Exp $ */
/* $NetBSD: subr.c,v 1.4 2010/09/03 07:15:18 manu Exp $ */
/*-
* Copyright (c) 2010 Emmanuel Dreyfus. All rights reserved.
@ -51,7 +51,8 @@ perfuse_new_pn(pu, parent)
DERR(EX_SOFTWARE, "puffs_pn_new failed");
(void)memset(pnd, 0, sizeof(*pnd));
TAILQ_INIT(&pnd->pnd_fh);
pnd->pnd_rfh = FUSE_UNKNOWN_FH;
pnd->pnd_wfh = FUSE_UNKNOWN_FH;
pnd->pnd_ino = PERFUSE_UNKNOWN_INO;
pnd->pnd_nlookup = 1;
pnd->pnd_parent = parent;
@ -79,8 +80,8 @@ perfuse_destroy_pn(pn)
if (pnd->pnd_all_fd != NULL)
free(pnd->pnd_all_fd);
#ifdef PERFUSE_DEBUG
if (!TAILQ_EMPTY(&pnd->pnd_fh))
DERRX(EX_SOFTWARE, "%s: non empty pnd_fh", __func__);
if (pnd->pnd_flags & PND_OPEN)
DERRX(EX_SOFTWARE, "%s: file open", __func__);
if (!TAILQ_EMPTY(&pnd->pnd_pcq))
DERRX(EX_SOFTWARE, "%s: non empty pnd_pcq", __func__);
@ -96,25 +97,30 @@ perfuse_destroy_pn(pn)
void
perfuse_new_fh(opc, fh)
perfuse_new_fh(opc, fh, mode)
puffs_cookie_t opc;
uint64_t fh;
int mode;
{
struct perfuse_node_data *pnd;
struct perfuse_file_handle *pfh;
if (fh == FUSE_UNKNOWN_FH)
return;
pnd = PERFUSE_NODE_DATA(opc);
pnd->pnd_flags |= PND_OPEN;
if ((pfh = malloc(sizeof(*pfh))) == NULL)
DERR(EX_OSERR, "malloc failed");
if (mode & FWRITE) {
if (pnd->pnd_flags & PND_WFH)
DERRX(EX_SOFTWARE, "%s: opc = %p, write fh already set",
__func__, (void *)opc);
pnd->pnd_wfh = fh;
pnd->pnd_flags |= PND_WFH;
}
pfh->pfh_fh = fh;
TAILQ_INSERT_TAIL(&pnd->pnd_fh, pfh, pfh_entries);
if (mode & FREAD) {
if (pnd->pnd_flags & PND_RFH)
DERRX(EX_SOFTWARE, "%s: opc = %p, read fh already set",
__func__, (void *)opc);
pnd->pnd_rfh = fh;
pnd->pnd_flags |= PND_RFH;
}
return;
}
@ -125,42 +131,51 @@ perfuse_destroy_fh(opc, fh)
uint64_t fh;
{
struct perfuse_node_data *pnd;
struct perfuse_file_handle *pfh;
pnd = PERFUSE_NODE_DATA(opc);
TAILQ_FOREACH(pfh, &pnd->pnd_fh, pfh_entries) {
if (pfh->pfh_fh == fh) {
TAILQ_REMOVE(&pnd->pnd_fh, pfh, pfh_entries);
free(pfh);
break;
}
if (fh == pnd->pnd_rfh) {
if (!(pnd->pnd_flags & PND_RFH) && (fh != FUSE_UNKNOWN_FH))
DERRX(EX_SOFTWARE,
"%s: opc = %p, unset rfh = %"PRIx64"",
__func__, (void *)opc, fh);
pnd->pnd_rfh = FUSE_UNKNOWN_FH;
pnd->pnd_flags &= ~PND_RFH;
}
if (TAILQ_EMPTY(&pnd->pnd_fh))
pnd->pnd_flags &= ~PND_OPEN;
if (fh == pnd->pnd_wfh) {
if (!(pnd->pnd_flags & PND_WFH) && (fh != FUSE_UNKNOWN_FH))
DERRX(EX_SOFTWARE,
"%s: opc = %p, unset wfh = %"PRIx64"",
__func__, (void *)opc, fh);
pnd->pnd_wfh = FUSE_UNKNOWN_FH;
pnd->pnd_flags &= ~PND_WFH;
}
if (pfh == NULL)
DERRX(EX_SOFTWARE,
"%s: unexistant fh = %"PRId64" (double close?)",
__func__, fh);
return;
}
uint64_t
perfuse_get_fh(opc)
perfuse_get_fh(opc, mode)
puffs_cookie_t opc;
int mode;
{
struct perfuse_node_data *pnd;
struct perfuse_file_handle *pfh;
uint64_t fh = FUSE_UNKNOWN_FH;
pnd = PERFUSE_NODE_DATA(opc);
if ((pfh = TAILQ_FIRST(&pnd->pnd_fh)) != NULL)
fh = pfh->pfh_fh;;
if (mode & FWRITE)
if (pnd->pnd_flags & PND_WFH)
return pnd->pnd_wfh;
return fh;
if (mode & FREAD) {
if (pnd->pnd_flags & PND_RFH)
return pnd->pnd_rfh;
if (pnd->pnd_flags & PND_WFH)
return pnd->pnd_wfh;
}
return FUSE_UNKNOWN_FH;
}