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