svn commit: r238693 - projects/fuse/sys/fs/fuse
Attilio Rao
attilio at FreeBSD.org
Sun Jul 22 14:41:36 UTC 2012
Author: attilio
Date: Sun Jul 22 14:41:35 2012
New Revision: 238693
URL: http://svn.freebsd.org/changeset/base/238693
Log:
Rewrite the fuse interface to the device:
- Remove entirely the usage of interface cloning
- Reimplement the passing of fdata using the devfs_*_cdevpriv interface
- Implement a real refcounting for the fdata objects representing the
former check on mnt and drv1 members
This requires that mount_fusefs now passes the filedescriptor number
when trying to mount a specific filesystem instance. Also, now all the
devices will open /dev/fuse specifically and nothing else.
This avoids some nasty races in the device cloning entries reusing from
the cloning list or alternatively a big wastage of memory in situations
where filesystems are frequently unmounted and mounted on long-running
systems.
Reported by: pho
Tested by: pho
Modified:
projects/fuse/sys/fs/fuse/fuse.h
projects/fuse/sys/fs/fuse/fuse_device.c
projects/fuse/sys/fs/fuse/fuse_ipc.c
projects/fuse/sys/fs/fuse/fuse_ipc.h
projects/fuse/sys/fs/fuse/fuse_main.c
projects/fuse/sys/fs/fuse/fuse_vfsops.c
Modified: projects/fuse/sys/fs/fuse/fuse.h
==============================================================================
--- projects/fuse/sys/fs/fuse/fuse.h Sun Jul 22 14:37:28 2012 (r238692)
+++ projects/fuse/sys/fs/fuse/fuse.h Sun Jul 22 14:41:35 2012 (r238693)
@@ -214,3 +214,6 @@ do { \
void fuse_ipc_init(void);
void fuse_ipc_destroy(void);
+
+int fuse_device_init(void);
+void fuse_device_destroy(void);
Modified: projects/fuse/sys/fs/fuse/fuse_device.c
==============================================================================
--- projects/fuse/sys/fs/fuse/fuse_device.c Sun Jul 22 14:37:28 2012 (r238692)
+++ projects/fuse/sys/fs/fuse/fuse_device.c Sun Jul 22 14:41:35 2012 (r238693)
@@ -83,9 +83,7 @@ __FBSDID("$FreeBSD$");
#define FUSE_DEBUG_MODULE DEVICE
#include "fuse_debug.h"
-static __inline int
-fuse_ohead_audit(struct fuse_out_header *ohead,
- struct uio *uio);
+static struct cdev *fuse_dev;
static d_open_t fuse_device_open;
static d_close_t fuse_device_close;
@@ -93,10 +91,6 @@ static d_poll_t fuse_device_poll;
static d_read_t fuse_device_read;
static d_write_t fuse_device_write;
-void
-fuse_device_clone(void *arg, struct ucred *cred, char *name,
- int namelen, struct cdev **dev);
-
static struct cdevsw fuse_device_cdevsw = {
.d_open = fuse_device_open,
.d_close = fuse_device_close,
@@ -108,22 +102,21 @@ static struct cdevsw fuse_device_cdevsw
.d_flags = D_NEEDMINOR,
};
-/*
- * This struct is not public, but we are eager to use it,
- * so we have to put its def here.
- */
-struct clonedevs {
- LIST_HEAD(, cdev) head;
-};
-
-struct clonedevs *fuseclones;
-
/****************************
*
* >>> Fuse device op defs
*
****************************/
+static void
+fdata_dtor(void *arg)
+{
+ struct fuse_data *fdata;
+
+ fdata = arg;
+ fdata_trydestroy(fdata);
+}
+
/*
* Resources are set up on a per-open basis
*/
@@ -131,31 +124,18 @@ static int
fuse_device_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
{
struct fuse_data *fdata;
-
- if (dev->si_usecount > 1)
- goto busy;
+ int error;
DEBUG("device %p\n", dev);
fdata = fdata_alloc(dev, td->td_ucred);
-
- FUSE_LOCK();
- if (fuse_get_devdata(dev)) {
+ error = devfs_set_cdevpriv(fdata, fdata_dtor);
+ if (error != 0)
fdata_trydestroy(fdata);
- FUSE_UNLOCK();
- goto busy;
- } else {
- fdata->dataflags |= FSESS_OPENED;
- dev->si_drv1 = fdata;
- }
- FUSE_UNLOCK();
-
- DEBUG("%s: device opened by thread %d.\n", dev->si_name, td->td_tid);
-
- return (0);
-
-busy:
- return (EBUSY);
+ else
+ DEBUG("%s: device opened by thread %d.\n", dev->si_name,
+ td->td_tid);
+ return (error);
}
static int
@@ -163,17 +143,16 @@ fuse_device_close(struct cdev *dev, int
{
struct fuse_data *data;
struct fuse_ticket *tick;
+ int error;
- data = fuse_get_devdata(dev);
+ error = devfs_get_cdevpriv((void **)&data);
+ if (error != 0)
+ return (error);
if (!data)
panic("no fuse data upon fuse device close");
- KASSERT(data->dataflags | FSESS_OPENED,
- ("fuse device is already closed upon close"));
fdata_set_dead(data);
FUSE_LOCK();
- data->dataflags &= ~FSESS_OPENED;
-
fuse_lck_mtx_lock(data->aw_mtx);
/* wakup poll()ers */
selwakeuppri(&data->ks_rsel, PZERO + 1);
@@ -188,9 +167,6 @@ fuse_device_close(struct cdev *dev, int
fuse_ticket_drop(tick);
}
fuse_lck_mtx_unlock(data->aw_mtx);
-
- dev->si_drv1 = NULL;
- fdata_trydestroy(data);
FUSE_UNLOCK();
DEBUG("%s: device closed by thread %d.\n", dev->si_name, td->td_tid);
@@ -201,9 +177,12 @@ int
fuse_device_poll(struct cdev *dev, int events, struct thread *td)
{
struct fuse_data *data;
- int revents = 0;
+ int error, revents = 0;
- data = fuse_get_devdata(dev);
+ error = devfs_get_cdevpriv((void **)&data);
+ if (error != 0)
+ return (events &
+ (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM));
if (events & (POLLIN | POLLRDNORM)) {
fuse_lck_mtx_lock(data->ms_mtx);
@@ -227,17 +206,19 @@ fuse_device_poll(struct cdev *dev, int e
int
fuse_device_read(struct cdev *dev, struct uio *uio, int ioflag)
{
- int err = 0;
+ int err;
struct fuse_data *data;
struct fuse_ticket *tick;
void *buf[] = {NULL, NULL, NULL};
int buflen[3];
int i;
- data = fuse_get_devdata(dev);
-
DEBUG("fuse device being read on thread %d\n", uio->uio_td->td_tid);
+ err = devfs_get_cdevpriv((void **)&data);
+ if (err != 0)
+ return (err);
+
fuse_lck_mtx_lock(data->ms_mtx);
again:
if (fdata_get_dead(data)) {
@@ -374,7 +355,9 @@ fuse_device_write(struct cdev *dev, stru
DEBUG("resid: %zd, iovcnt: %d, thread: %d\n",
uio->uio_resid, uio->uio_iovcnt, uio->uio_td->td_tid);
- data = fuse_get_devdata(dev);
+ err = devfs_get_cdevpriv((void **)&data);
+ if (err != 0)
+ return (err);
if (uio->uio_resid < sizeof(struct fuse_out_header)) {
DEBUG("got less than a header!\n");
@@ -444,81 +427,21 @@ fuse_device_write(struct cdev *dev, stru
return (err);
}
-/*
- * Modeled after tunclone() of net/if_tun.c ...
- * boosted with a hack so that devices can be reused.
- */
-void
-fuse_device_clone(void *arg, struct ucred *cred, char *name, int namelen,
- struct cdev **dev)
+int
+fuse_device_init(void)
{
- /*
- * Why cloning? We do need per-open info, but we could as well put our
- * hands on the file struct assigned to an open by implementing
- * d_fdopen instead of d_open.
- *
- * From that on, the usual way to per-open (that is, file aware)
- * I/O would be pushing our preferred set of ops into the f_op
- * field of that file at open time. But that wouldn't work in
- * FreeBSD, as the devfs open routine (which is the one who calls
- * the device's d_(fd)open) overwrites that f_op with its own
- * file ops mercilessly.
- *
- * Yet... even if we could get devfs to keep our file ops intact,
- * I'd still say cloning is better. It makes fuse daemons' identity
- * explicit and globally visible to userspace, and we are not forced
- * to get the mount done by the daemon itself like in linux (where
- * I/O is file aware by default). (The possibilities of getting the
- * daemon do the mount or getting the mount util spawn the daemon
- * are still open, of course; I guess I will go for the latter
- * appcocroach.)
- */
- int i, unit;
-
- if (*dev != NULL)
- return;
-
- if (strcmp(name, "fuse") == 0) {
- struct cdev *xdev;
-
- unit = -1;
+ fuse_dev = make_dev(&fuse_device_cdevsw, 0, UID_ROOT, GID_OPERATOR,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, "fuse");
+ if (fuse_dev == NULL)
+ return (ENOMEM);
+ return (0);
+}
- /*
- * Before falling back to the standard routine, we try
- * to find an existing free device by ourselves, so that
- * it will be reused instead of having the clone machinery
- * dummily spawn a new one.
- */
- dev_lock();
- LIST_FOREACH(xdev, &fuseclones->head, si_clone) {
- KASSERT(xdev->si_flags & SI_CLONELIST,
- ("Dev %p(%s) should be on clonelist", xdev, xdev->si_name));
-
- if (!xdev->si_drv1) {
- unit = dev2unit(xdev);
- break;
- }
- }
- dev_unlock();
- } else if (dev_stdclone(name, NULL, "fuse", &unit) != 1) {
- return;
- }
- /* find any existing device, or allocate new unit number */
- i = clone_create(&fuseclones, &fuse_device_cdevsw, &unit, dev, 0);
- if (i) {
- *dev = make_dev(&fuse_device_cdevsw,
- unit,
- UID_ROOT, GID_OPERATOR,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
- "fuse%d", unit);
- if (*dev == NULL)
- return;
- }
- KASSERT(*dev, ("no device after apparently successful cloning"));
- dev_ref(*dev);
- (*dev)->si_drv1 = NULL;
- (*dev)->si_flags |= SI_CHEAPCLONE;
+void
+fuse_device_destroy(void)
+{
- DEBUG("clone done: %d\n", unit);
+ MPASS(fuse_dev != NULL);
+ destroy_dev(fuse_dev);
}
Modified: projects/fuse/sys/fs/fuse/fuse_ipc.c
==============================================================================
--- projects/fuse/sys/fs/fuse/fuse_ipc.c Sun Jul 22 14:37:28 2012 (r238692)
+++ projects/fuse/sys/fs/fuse/fuse_ipc.c Sun Jul 22 14:41:35 2012 (r238693)
@@ -399,6 +399,7 @@ fdata_alloc(struct cdev *fdev, struct uc
data->daemoncred = crhold(cred);
data->daemon_timeout = FUSE_DEFAULT_DAEMON_TIMEOUT;
sx_init(&data->rename_lock, "fuse rename lock");
+ data->ref = 1;
return data;
}
@@ -409,16 +410,11 @@ fdata_trydestroy(struct fuse_data *data)
DEBUG("data=%p data.mp=%p data.fdev=%p data.flags=%04x\n",
data, data->mp, data->fdev, data->dataflags);
- if (data->mp != NULL) {
- MPASS(data->mp->mnt_data == data);
- return;
- }
- if (data->fdev->si_drv1 != NULL) {
- MPASS(data->fdev->si_drv1 == data);
- return;
- }
DEBUG("destroy: data=%p\n", data);
- MPASS((data->dataflags & FSESS_OPENED) == 0);
+ data->ref--;
+ MPASS(data->ref >= 0);
+ if (data->ref != 0)
+ return;
/* Driving off stage all that stuff thrown at device... */
mtx_destroy(&data->ms_mtx);
Modified: projects/fuse/sys/fs/fuse/fuse_ipc.h
==============================================================================
--- projects/fuse/sys/fs/fuse/fuse_ipc.h Sun Jul 22 14:37:28 2012 (r238692)
+++ projects/fuse/sys/fs/fuse/fuse_ipc.h Sun Jul 22 14:41:35 2012 (r238693)
@@ -170,6 +170,7 @@ struct fuse_data {
struct vnode *vroot;
struct ucred *daemoncred;
int dataflags;
+ int ref;
struct mtx ms_mtx;
STAILQ_HEAD(, fuse_ticket) ms_head;
@@ -196,7 +197,7 @@ struct fuse_data {
};
#define FSESS_DEAD 0x0001 /* session is to be closed */
-#define FSESS_OPENED 0x0002 /* session device has been opened */
+#define FSESS_UNUSED0 0x0002 /* unused */
#define FSESS_INITED 0x0004 /* session has been inited */
#define FSESS_DAEMON_CAN_SPY 0x0010 /* let non-owners access this fs */
/* (and being observed by the daemon) */
@@ -217,13 +218,6 @@ extern int fuse_fix_broken_io;
static __inline__
struct fuse_data *
-fuse_get_devdata(struct cdev *fdev)
-{
- return fdev->si_drv1;
-}
-
-static __inline__
-struct fuse_data *
fuse_get_mpdata(struct mount *mp)
{
return mp->mnt_data;
Modified: projects/fuse/sys/fs/fuse/fuse_main.c
==============================================================================
--- projects/fuse/sys/fs/fuse/fuse_main.c Sun Jul 22 14:37:28 2012 (r238692)
+++ projects/fuse/sys/fs/fuse/fuse_main.c Sun Jul 22 14:41:35 2012 (r238693)
@@ -79,15 +79,9 @@ static int fuse_loader(struct module *m,
struct mtx fuse_mtx;
-
-extern void
-fuse_device_clone(void *arg, struct ucred *cred, char *name,
- int namelen, struct cdev **dev);
-
extern struct vfsops fuse_vfsops;
extern struct cdevsw fuse_cdevsw;
extern struct vop_vector fuse_vnops;
-extern struct clonedevs *fuseclones;
extern int fuse_pbuf_freecnt;
static struct vfsconf fuse_vfsconf = {
@@ -113,10 +107,8 @@ static void
fuse_bringdown(eventhandler_tag eh_tag)
{
- EVENTHANDLER_DEREGISTER(dev_clone, eh_tag);
-
fuse_ipc_destroy();
- clone_cleanup(&fuseclones);
+ fuse_device_destroy();
mtx_destroy(&fuse_mtx);
}
@@ -129,14 +121,11 @@ fuse_loader(struct module *m, int what,
switch (what) {
case MOD_LOAD: /* kldload */
fuse_pbuf_freecnt = nswbuf / 2 + 1;
- clone_setup(&fuseclones);
mtx_init(&fuse_mtx, "fuse_mtx", NULL, MTX_DEF);
- eh_tag = EVENTHANDLER_REGISTER(dev_clone, fuse_device_clone, 0,
- 1000);
- if (eh_tag == NULL) {
- clone_cleanup(&fuseclones);
+ err = fuse_device_init();
+ if (err) {
mtx_destroy(&fuse_mtx);
- return (ENOMEM);
+ return (err);
}
fuse_ipc_init();
Modified: projects/fuse/sys/fs/fuse/fuse_vfsops.c
==============================================================================
--- projects/fuse/sys/fs/fuse/fuse_vfsops.c Sun Jul 22 14:37:28 2012 (r238692)
+++ projects/fuse/sys/fs/fuse/fuse_vfsops.c Sun Jul 22 14:41:35 2012 (r238693)
@@ -62,7 +62,9 @@ __FBSDID("$FreeBSD$");
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/kernel.h>
+#include <sys/capability.h>
#include <sys/conf.h>
+#include <sys/filedesc.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/queue.h>
@@ -202,27 +204,31 @@ fuse_getdevice(const char *fspec, struct
static int
fuse_vfsop_mount(struct mount *mp)
{
- int err = 0;
-
-#if __FreeBSD_version >= 900040
- uint64_t mntopts = 0, __mntopts = 0;
-
-#else
- int mntopts = 0, __mntopts = 0;
+ int err;
-#endif
- int max_read_set = 0;
- uint32_t max_read = ~0;
+ uint64_t mntopts, __mntopts;
+ int max_read_set;
+ uint32_t max_read;
int daemon_timeout;
+ int fd;
size_t len;
struct cdev *fdev;
struct fuse_data *data;
- struct thread *td = curthread;
- char *fspec, *subtype = NULL;
+ struct thread *td;
+ struct file *fp, *fptmp;
+ char *fspec, *subtype;
struct vfsoptlist *opts;
+ subtype = NULL;
+ max_read_set = 0;
+ max_read = ~0;
+ err = 0;
+ mntopts = 0;
+ __mntopts = 0;
+ td = curthread;
+
fuse_trace_printf_vfsop();
if (mp->mnt_flag & MNT_UPDATE)
@@ -245,6 +251,12 @@ fuse_vfsop_mount(struct mount *mp)
if (!fspec)
return err;
+ /* `fd' contains the filedescriptor for this session; REQUIRED */
+ if (!vfs_scanopt(opts, "fd", "%d", &fd)) {
+ printf("coglione\n");
+ return err;
+ }
+
err = fuse_getdevice(fspec, td, &fdev);
if (err != 0)
return err;
@@ -274,22 +286,26 @@ fuse_vfsop_mount(struct mount *mp)
daemon_timeout = FUSE_DEFAULT_DAEMON_TIMEOUT;
}
subtype = vfs_getopts(opts, "subtype=", &err);
- err = 0;
DEBUG2G("mntopts 0x%jx\n", (uintmax_t)mntopts);
+ err = fget(td, fd, CAP_READ, &fp);
+ if (err != 0) {
+ DEBUG("invalid or not opened device: data=%p\n", data);
+ goto out;
+ }
+ fptmp = td->td_fpop;
+ td->td_fpop = fp;
+ err = devfs_get_cdevpriv((void **)&data);
+ td->td_fpop = fptmp;
+ fdrop(fp, td);
FUSE_LOCK();
- data = fuse_get_devdata(fdev);
- if (data == NULL || data->mp != NULL ||
- (data->dataflags & FSESS_OPENED) == 0) {
+ if (err != 0 || data == NULL || data->mp != NULL) {
DEBUG("invalid or not opened device: data=%p data.mp=%p\n",
data, data != NULL ? data->mp : NULL);
err = ENXIO;
FUSE_UNLOCK();
goto out;
- } else {
- DEBUG("set mp: data=%p mp=%p\n", data, mp);
- data->mp = mp;
}
if (fdata_get_dead(data)) {
DEBUG("device is dead during mount: data=%p\n", data);
@@ -312,6 +328,7 @@ fuse_vfsop_mount(struct mount *mp)
/* We need this here as this slot is used by getnewvnode() */
mp->mnt_stat.f_iosize = PAGE_SIZE;
mp->mnt_data = data;
+ data->ref++;
data->mp = mp;
data->dataflags |= mntopts;
data->max_read = max_read;
More information about the svn-src-projects
mailing list