fusefs: replace the fufh table with a linked list

The FUSE protocol allows each open file descriptor to have a unique file
handle.  On FreeBSD, these file handles must all be stored in the vnode.
The old method (also used by OSX and OpenBSD) is to store them all in a
small array.  But that limits the total number that can be stored.  This
commit replaces the array with a linked list (a technique also used by
Illumos).  There is not yet any change in functionality, but this is the
first step to fixing several bugs.

PR:		236329, 236340, 236381, 236560, 236844
Discussed with:	cem
Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Alan Somers 2019-03-31 03:19:10 +00:00
parent 208070583f
commit 1cedd6dfac
6 changed files with 55 additions and 54 deletions

View File

@ -82,6 +82,8 @@ __FBSDID("$FreeBSD$");
#include "fuse_ipc.h"
#include "fuse_node.h"
MALLOC_DEFINE(M_FUSE_FILEHANDLE, "fuse_filefilehandle", "FUSE file handle");
SDT_PROVIDER_DECLARE(fuse);
/*
* Fuse trace probe:
@ -157,16 +159,14 @@ fuse_filehandle_close(struct vnode *vp, fufh_type_t fufh_type,
{
struct fuse_dispatcher fdi;
struct fuse_release_in *fri;
struct fuse_vnode_data *fvdat = VTOFUD(vp);
struct fuse_filehandle *fufh = NULL;
int err = 0;
int op = FUSE_RELEASE;
fufh = &(fvdat->fufh[fufh_type]);
if (!FUFH_IS_VALID(fufh)) {
panic("FUSE: filehandle_put called on invalid fufh (type=%d)",
fufh_type);
if (fuse_filehandle_get(vp, fufh_type, &fufh)) {
panic("FUSE: fuse_filehandle_close called on invalid fufh "
"(type=%d)", fufh_type);
/* NOTREACHED */
}
if (fuse_isdeadfs(vp)) {
@ -185,8 +185,8 @@ fuse_filehandle_close(struct vnode *vp, fufh_type_t fufh_type,
out:
atomic_subtract_acq_int(&fuse_fh_count, 1);
fufh->fh_id = (uint64_t)-1;
fufh->fh_type = FUFH_INVALID;
LIST_REMOVE(fufh, next);
free(fufh, M_FUSE_FILEHANDLE);
return err;
}
@ -194,31 +194,22 @@ out:
int
fuse_filehandle_valid(struct vnode *vp, fufh_type_t fufh_type)
{
struct fuse_vnode_data *fvdat = VTOFUD(vp);
struct fuse_filehandle *fufh;
fufh = &(fvdat->fufh[fufh_type]);
return FUFH_IS_VALID(fufh);
return (0 == fuse_filehandle_get(vp, fufh_type, NULL));
}
/*
* Check for a valid file handle, first the type requested, but if that
* isn't valid, try for FUFH_RDWR.
* Return the FUFH type that is valid or FUFH_INVALID if there are none.
* This is a variant of fuse_filehandle_vaild() analogous to
* This is a variant of fuse_filehandle_valid() analogous to
* fuse_filehandle_getrw().
*/
fufh_type_t
fuse_filehandle_validrw(struct vnode *vp, fufh_type_t fufh_type)
{
struct fuse_vnode_data *fvdat = VTOFUD(vp);
struct fuse_filehandle *fufh;
fufh = &fvdat->fufh[fufh_type];
if (FUFH_IS_VALID(fufh) != 0)
if (fuse_filehandle_get(vp, fufh_type, NULL) == 0)
return (fufh_type);
fufh = &fvdat->fufh[FUFH_RDWR];
if (FUFH_IS_VALID(fufh) != 0)
if (fuse_filehandle_get(vp, FUFH_RDWR, NULL) == 0)
return (FUFH_RDWR);
return (FUFH_INVALID);
}
@ -230,8 +221,14 @@ fuse_filehandle_get(struct vnode *vp, fufh_type_t fufh_type,
struct fuse_vnode_data *fvdat = VTOFUD(vp);
struct fuse_filehandle *fufh;
fufh = &(fvdat->fufh[fufh_type]);
if (!FUFH_IS_VALID(fufh))
/* TODO: Find a list entry with the same mode, pid, gid, and uid */
/* Fallback: find a list entry with the right mode */
LIST_FOREACH(fufh, &fvdat->handles, next) {
if (fufh->mode == fufh_type)
break;
}
if (fufh == NULL)
return EBADF;
if (fufhp != NULL)
*fufhp = fufh;
@ -242,14 +239,12 @@ int
fuse_filehandle_getrw(struct vnode *vp, fufh_type_t fufh_type,
struct fuse_filehandle **fufhp)
{
struct fuse_vnode_data *fvdat = VTOFUD(vp);
struct fuse_filehandle *fufh;
int err;
fufh = &(fvdat->fufh[fufh_type]);
if (!FUFH_IS_VALID(fufh)) {
fufh_type = FUFH_RDWR;
}
return fuse_filehandle_get(vp, fufh_type, fufhp);
err = fuse_filehandle_get(vp, fufh_type, fufhp);
if (err)
err = fuse_filehandle_get(vp, FUFH_RDWR, fufhp);
return err;
}
void
@ -259,13 +254,16 @@ fuse_filehandle_init(struct vnode *vp, fufh_type_t fufh_type,
struct fuse_vnode_data *fvdat = VTOFUD(vp);
struct fuse_filehandle *fufh;
fufh = &(fvdat->fufh[fufh_type]);
MPASS(!FUFH_IS_VALID(fufh));
fufh = malloc(sizeof(struct fuse_filehandle), M_FUSE_FILEHANDLE,
M_WAITOK);
MPASS(fufh != NULL);
fufh->fh_id = fh_id;
fufh->fh_type = fufh_type;
fufh->mode = fufh_type;
/* TODO: initialize fufh credentials and open flags */
if (!FUFH_IS_VALID(fufh)) {
panic("FUSE: init: invalid filehandle id (type=%d)", fufh_type);
}
LIST_INSERT_HEAD(&fvdat->handles, fufh, next);
if (fufhp != NULL)
*fufhp = fufh;

View File

@ -78,11 +78,24 @@ _Static_assert(FUFH_WRONLY == O_WRONLY, "WRONLY");
_Static_assert(FUFH_RDWR == O_RDWR, "RDWR");
struct fuse_filehandle {
LIST_ENTRY(fuse_filehandle) next;
/* The filehandle returned by FUSE_OPEN */
uint64_t fh_id;
fufh_type_t fh_type;
/* flags returned by FUSE_OPEN */
uint32_t fuse_open_flags;
/* The mode used to open(2) the file (using O_RDONLY, not FREAD) */
uint32_t mode;
/* Credentials used to open the file */
gid_t gid;
pid_t pid;
uid_t uid;
};
#define FUFH_IS_VALID(f) ((f)->fh_type != FUFH_INVALID)
#define FUFH_IS_VALID(f) ((f)->mode != FUFH_INVALID)
static inline fufh_type_t
fuse_filehandle_xlate_from_fflags(int fflags)

View File

@ -285,7 +285,6 @@ fuse_internal_fsync(struct vnode *vp,
struct fuse_fsync_in *ffsi;
struct fuse_dispatcher fdi;
struct fuse_filehandle *fufh;
struct fuse_vnode_data *fvdat = VTOFUD(vp);
int op = FUSE_FSYNC;
int type = 0;
int err = 0;
@ -295,8 +294,7 @@ fuse_internal_fsync(struct vnode *vp,
return 0;
}
for (type = 0; type < FUFH_MAXTYPE; type++) {
fufh = &(fvdat->fufh[type]);
if (FUFH_IS_VALID(fufh)) {
if (fuse_filehandle_get(vp, type, &fufh) == 0) {
if (vnode_isdir(vp)) {
op = FUSE_FSYNCDIR;
}
@ -454,11 +452,7 @@ fuse_internal_remove(struct vnode *dvp,
enum fuse_opcode op)
{
struct fuse_dispatcher fdi;
struct fuse_vnode_data *fvdat;
int err;
err = 0;
fvdat = VTOFUD(vp);
int err = 0;
fdisp_init(&fdi, cnp->cn_namelen + 1);
fdisp_make_vp(&fdi, op, dvp, cnp->cn_thread, cnp->cn_cred);

View File

@ -174,9 +174,8 @@ static void
fuse_vnode_init(struct vnode *vp, struct fuse_vnode_data *fvdat,
uint64_t nodeid, enum vtype vtyp)
{
int i;
fvdat->nid = nodeid;
LIST_INIT(&fvdat->handles);
vattr_null(&fvdat->cached_attrs);
if (nodeid == FUSE_ROOT_ID) {
vp->v_vflag |= VV_ROOT;
@ -184,9 +183,6 @@ fuse_vnode_init(struct vnode *vp, struct fuse_vnode_data *fvdat,
vp->v_type = vtyp;
vp->v_data = fvdat;
for (i = 0; i < FUFH_MAXTYPE; i++)
fvdat->fufh[i].fh_type = FUFH_INVALID;
atomic_add_acq_int(&fuse_node_count, 1);
}
@ -196,6 +192,8 @@ fuse_vnode_destroy(struct vnode *vp)
struct fuse_vnode_data *fvdat = vp->v_data;
vp->v_data = NULL;
KASSERT(LIST_EMPTY(&fvdat->handles),
("Destroying fuse vnode with open files!"));
free(fvdat, M_FUSEVN);
atomic_subtract_acq_int(&fuse_node_count, 1);
@ -314,7 +312,7 @@ void
fuse_vnode_open(struct vnode *vp, int32_t fuse_open_flags, struct thread *td)
{
/*
* Funcation is called for every vnode open.
* Function is called for every vnode open.
* Merge fuse_open_flags it may be 0
*/
/*

View File

@ -80,7 +80,8 @@ struct fuse_vnode_data {
uint64_t parent_nid;
/** I/O **/
struct fuse_filehandle fufh[FUFH_MAXTYPE];
/* List of file data for each of the vnode's open file descriptors */
LIST_HEAD(, fuse_filehandle) handles;
/** flags **/
uint32_t flag;

View File

@ -606,8 +606,7 @@ fuse_vnop_inactive(struct vop_inactive_args *ap)
int type, need_flush = 1;
for (type = 0; type < FUFH_MAXTYPE; type++) {
fufh = &(fvdat->fufh[type]);
if (FUFH_IS_VALID(fufh)) {
if (!fuse_filehandle_get(vp, type, &fufh)) {
if (need_flush && vp->v_type == VREG) {
if ((VTOFUD(vp)->flag & FN_SIZECHANGE) != 0) {
fuse_vnode_savesize(vp, NULL);
@ -1396,7 +1395,6 @@ fuse_vnop_reclaim(struct vop_reclaim_args *ap)
struct thread *td = ap->a_td;
struct fuse_vnode_data *fvdat = VTOFUD(vp);
struct fuse_filehandle *fufh = NULL;
int type;
@ -1404,8 +1402,7 @@ fuse_vnop_reclaim(struct vop_reclaim_args *ap)
panic("FUSE: no vnode data during recycling");
}
for (type = 0; type < FUFH_MAXTYPE; type++) {
fufh = &(fvdat->fufh[type]);
if (FUFH_IS_VALID(fufh)) {
if (fuse_filehandle_get(vp, type, NULL) == 0) {
printf("FUSE: vnode being reclaimed but fufh (type=%d) is valid",
type);
fuse_filehandle_close(vp, type, td, NULL);