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:
parent
208070583f
commit
1cedd6dfac
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
*/
|
||||
/*
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user