* [patch 00/12] fuse update
@ 2006-10-16 16:27 Miklos Szeredi
2006-10-16 16:27 ` [patch 01/12] fuse: fix hang on SMP Miklos Szeredi
` (11 more replies)
0 siblings, 12 replies; 16+ messages in thread
From: Miklos Szeredi @ 2006-10-16 16:27 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel
Hi Andrew,
This patch series syncs the kernel code with the upcoming fuse-2.6.0
release.
Patches 1-6 are bug fixes and should go into 2.6.19.
Patch 1 fixes a system hang on SMP. Though triggered very rarely,
this is a serious condition, so I plan submitting this patch to
-stable also.
Patches 7-8 are small updates.
Patches 9-11 add better support for block device based filesystems
(such as ntfs-3g).
Thanks,
Miklos
--
^ permalink raw reply [flat|nested] 16+ messages in thread
* [patch 01/12] fuse: fix hang on SMP
2006-10-16 16:27 [patch 00/12] fuse update Miklos Szeredi
@ 2006-10-16 16:27 ` Miklos Szeredi
2006-10-16 23:51 ` Andrew Morton
2006-10-16 16:27 ` [patch 02/12] document i_size_write locking rules Miklos Szeredi
` (10 subsequent siblings)
11 siblings, 1 reply; 16+ messages in thread
From: Miklos Szeredi @ 2006-10-16 16:27 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel
[-- Attachment #1: fuse_i_size_write_fix.patch --]
[-- Type: text/plain, Size: 4856 bytes --]
Fuse didn't always call i_size_write() with i_mutex held which caused
rare hangs on SMP/32bit. This bug has been present since fuse-2.2,
well before being merged into mainline.
The simplest solution is to protect i_size_write() with the
per-connection spinlock. Using i_mutex for this purpose would require
some restructuring of the code and I'm not even sure it's always safe
to acquire i_mutex in all places i_size needs to be set.
Since most of vmtruncate is already duplicated for other reasons,
duplicate the remaining part as well, making all i_size_write() calls
internal to fuse.
Using i_size_write() was unnecessary in fuse_init_inode(), since this
function is only called on a newly created locked inode.
Reported by a few people over the years, but special thanks to Dana
Henriksen who was persistent enough in helping me debug it.
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
---
Index: linux/fs/fuse/dir.c
===================================================================
--- linux.orig/fs/fuse/dir.c 2006-10-16 15:56:10.000000000 +0200
+++ linux/fs/fuse/dir.c 2006-10-16 16:06:11.000000000 +0200
@@ -935,14 +935,30 @@ static void iattr_to_fattr(struct iattr
}
}
+static void fuse_vmtruncate(struct inode *inode, loff_t offset)
+{
+ struct fuse_conn *fc = get_fuse_conn(inode);
+ int need_trunc;
+
+ spin_lock(&fc->lock);
+ need_trunc = inode->i_size > offset;
+ i_size_write(inode, offset);
+ spin_unlock(&fc->lock);
+
+ if (need_trunc) {
+ struct address_space *mapping = inode->i_mapping;
+ unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
+ truncate_inode_pages(mapping, offset);
+ }
+}
+
/*
* Set attributes, and at the same time refresh them.
*
* Truncation is slightly complicated, because the 'truncate' request
* may fail, in which case we don't want to touch the mapping.
- * vmtruncate() doesn't allow for this case. So do the rlimit
- * checking by hand and call vmtruncate() only after the file has
- * actually been truncated.
+ * vmtruncate() doesn't allow for this case, so do the rlimit checking
+ * and the actual truncation by hand.
*/
static int fuse_setattr(struct dentry *entry, struct iattr *attr)
{
@@ -993,12 +1009,8 @@ static int fuse_setattr(struct dentry *e
make_bad_inode(inode);
err = -EIO;
} else {
- if (is_truncate) {
- loff_t origsize = i_size_read(inode);
- i_size_write(inode, outarg.attr.size);
- if (origsize > outarg.attr.size)
- vmtruncate(inode, outarg.attr.size);
- }
+ if (is_truncate)
+ fuse_vmtruncate(inode, outarg.attr.size);
fuse_change_attributes(inode, &outarg.attr);
fi->i_time = time_to_jiffies(outarg.attr_valid,
outarg.attr_valid_nsec);
Index: linux/fs/fuse/file.c
===================================================================
--- linux.orig/fs/fuse/file.c 2006-10-16 15:56:10.000000000 +0200
+++ linux/fs/fuse/file.c 2006-10-16 15:58:45.000000000 +0200
@@ -481,8 +481,10 @@ static int fuse_commit_write(struct file
err = -EIO;
if (!err) {
pos += count;
- if (pos > i_size_read(inode))
+ spin_lock(&fc->lock);
+ if (pos > inode->i_size)
i_size_write(inode, pos);
+ spin_unlock(&fc->lock);
if (offset == 0 && to == PAGE_CACHE_SIZE) {
clear_page_dirty(page);
@@ -586,8 +588,12 @@ static ssize_t fuse_direct_io(struct fil
}
fuse_put_request(fc, req);
if (res > 0) {
- if (write && pos > i_size_read(inode))
- i_size_write(inode, pos);
+ if (write) {
+ spin_lock(&fc->lock);
+ if (pos > inode->i_size)
+ i_size_write(inode, pos);
+ spin_unlock(&fc->lock);
+ }
*ppos = pos;
}
fuse_invalidate_attr(inode);
Index: linux/fs/fuse/inode.c
===================================================================
--- linux.orig/fs/fuse/inode.c 2006-10-16 15:56:10.000000000 +0200
+++ linux/fs/fuse/inode.c 2006-10-16 15:57:12.000000000 +0200
@@ -109,6 +109,7 @@ static int fuse_remount_fs(struct super_
void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
{
+ struct fuse_conn *fc = get_fuse_conn(inode);
if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size)
invalidate_inode_pages(inode->i_mapping);
@@ -117,7 +118,9 @@ void fuse_change_attributes(struct inode
inode->i_nlink = attr->nlink;
inode->i_uid = attr->uid;
inode->i_gid = attr->gid;
+ spin_lock(&fc->lock);
i_size_write(inode, attr->size);
+ spin_unlock(&fc->lock);
inode->i_blocks = attr->blocks;
inode->i_atime.tv_sec = attr->atime;
inode->i_atime.tv_nsec = attr->atimensec;
@@ -130,7 +133,7 @@ void fuse_change_attributes(struct inode
static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
{
inode->i_mode = attr->mode & S_IFMT;
- i_size_write(inode, attr->size);
+ inode->i_size = attr->size;
if (S_ISREG(inode->i_mode)) {
fuse_init_common(inode);
fuse_init_file_inode(inode);
--
^ permalink raw reply [flat|nested] 16+ messages in thread
* [patch 02/12] document i_size_write locking rules
2006-10-16 16:27 [patch 00/12] fuse update Miklos Szeredi
2006-10-16 16:27 ` [patch 01/12] fuse: fix hang on SMP Miklos Szeredi
@ 2006-10-16 16:27 ` Miklos Szeredi
2006-10-16 16:27 ` [patch 03/12] fuse: locking fix for nlookup Miklos Szeredi
` (9 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Miklos Szeredi @ 2006-10-16 16:27 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel
[-- Attachment #1: document_i_size_write_locking.patch --]
[-- Type: text/plain, Size: 921 bytes --]
Unless someone reads the documentation for write_seqcount_{begin,end}
it is not obvious, that i_size_write() needs locking. Especially,
that lack of such locking can result in a system hang.
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
---
Index: linux/include/linux/fs.h
===================================================================
--- linux.orig/include/linux/fs.h 2006-10-16 12:53:03.000000000 +0200
+++ linux/include/linux/fs.h 2006-10-16 14:10:22.000000000 +0200
@@ -670,7 +670,11 @@ static inline loff_t i_size_read(struct
#endif
}
-
+/*
+ * NOTE: unlike i_size_read(), i_size_write() does need locking around it
+ * (normally i_mutex), otherwise on 32bit/SMP an update of i_size_seqcount
+ * can be lost, resulting in subsequent i_size_read() calls spinning forever.
+ */
static inline void i_size_write(struct inode *inode, loff_t i_size)
{
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
--
^ permalink raw reply [flat|nested] 16+ messages in thread
* [patch 03/12] fuse: locking fix for nlookup
2006-10-16 16:27 [patch 00/12] fuse update Miklos Szeredi
2006-10-16 16:27 ` [patch 01/12] fuse: fix hang on SMP Miklos Szeredi
2006-10-16 16:27 ` [patch 02/12] document i_size_write locking rules Miklos Szeredi
@ 2006-10-16 16:27 ` Miklos Szeredi
2006-10-16 16:27 ` [patch 04/12] fuse: fix spurious BUG Miklos Szeredi
` (8 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Miklos Szeredi @ 2006-10-16 16:27 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel
[-- Attachment #1: fuse_protect_nlookup.patch --]
[-- Type: text/plain, Size: 1189 bytes --]
An inode could be returned by independent parallel lookups, in this
case an update of the lookup counter could be lost resulting in a
memory leak in userspace.
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
---
Index: linux/fs/fuse/dir.c
===================================================================
--- linux.orig/fs/fuse/dir.c 2006-10-16 16:06:11.000000000 +0200
+++ linux/fs/fuse/dir.c 2006-10-16 16:08:00.000000000 +0200
@@ -163,7 +163,9 @@ static int fuse_dentry_revalidate(struct
fuse_send_forget(fc, req, outarg.nodeid, 1);
return 0;
}
+ spin_lock(&fc->lock);
fi->nlookup ++;
+ spin_unlock(&fc->lock);
}
fuse_put_request(fc, req);
if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
Index: linux/fs/fuse/inode.c
===================================================================
--- linux.orig/fs/fuse/inode.c 2006-10-16 15:57:12.000000000 +0200
+++ linux/fs/fuse/inode.c 2006-10-16 16:08:00.000000000 +0200
@@ -195,7 +195,9 @@ struct inode *fuse_iget(struct super_blo
}
fi = get_fuse_inode(inode);
+ spin_lock(&fc->lock);
fi->nlookup ++;
+ spin_unlock(&fc->lock);
fuse_change_attributes(inode, attr);
return inode;
}
--
^ permalink raw reply [flat|nested] 16+ messages in thread
* [patch 04/12] fuse: fix spurious BUG
2006-10-16 16:27 [patch 00/12] fuse update Miklos Szeredi
` (2 preceding siblings ...)
2006-10-16 16:27 ` [patch 03/12] fuse: locking fix for nlookup Miklos Szeredi
@ 2006-10-16 16:27 ` Miklos Szeredi
2006-10-16 16:27 ` [patch 05/12] fuse: fix handling of moved directory Miklos Szeredi
` (7 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Miklos Szeredi @ 2006-10-16 16:27 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel
[-- Attachment #1: fuse_iget_race_fix.patch --]
[-- Type: text/plain, Size: 1205 bytes --]
Fix a spurious BUG in an unlikely race, where at least three parallel
lookups return the same inode, but with different file type. This has
not yet been observed in real life.
Allowing unlimited retries could delay fuse_iget() indefinitely, but
this is really for the broken userspace filesystem to worry about.
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
---
Index: linux/fs/fuse/inode.c
===================================================================
--- linux.orig/fs/fuse/inode.c 2006-10-16 16:08:00.000000000 +0200
+++ linux/fs/fuse/inode.c 2006-10-16 16:08:08.000000000 +0200
@@ -172,7 +172,6 @@ struct inode *fuse_iget(struct super_blo
struct inode *inode;
struct fuse_inode *fi;
struct fuse_conn *fc = get_fuse_conn_super(sb);
- int retried = 0;
retry:
inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid);
@@ -186,11 +185,9 @@ struct inode *fuse_iget(struct super_blo
fuse_init_inode(inode, attr);
unlock_new_inode(inode);
} else if ((inode->i_mode ^ attr->mode) & S_IFMT) {
- BUG_ON(retried);
/* Inode has changed type, any I/O on the old should fail */
make_bad_inode(inode);
iput(inode);
- retried = 1;
goto retry;
}
--
^ permalink raw reply [flat|nested] 16+ messages in thread
* [patch 05/12] fuse: fix handling of moved directory
2006-10-16 16:27 [patch 00/12] fuse update Miklos Szeredi
` (3 preceding siblings ...)
2006-10-16 16:27 ` [patch 04/12] fuse: fix spurious BUG Miklos Szeredi
@ 2006-10-16 16:27 ` Miklos Szeredi
2006-10-16 16:27 ` [patch 06/12] fuse: fix dereferencing dentry parent Miklos Szeredi
` (6 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Miklos Szeredi @ 2006-10-16 16:27 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel
[-- Attachment #1: fuse_dir_alias_fix.patch --]
[-- Type: text/plain, Size: 5000 bytes --]
Fuse considered it an error (EIO) if lookup returned a directory
inode, to which a dentry already refered. This is because directory
aliases are not allowed.
But in a network filesystem this could happen legitimately, if a
directory is moved on a remote client. This patch attempts to relax
the restriction by trying to first evict the offending alias from the
cache. If this fails, it still returns an error (EBUSY).
A rarer situation is if an mkdir races with an indenpendent lookup,
which finds the newly created directory already moved. In this
situation the mkdir should return success, but that would be
incorrect, since the dentry cannot be instantiated, so return EBUSY.
Previously checking for a directory alias and instantiation of the
dentry weren't done atomically in lookup/mkdir, hence two such calls
racing with each other could create aliased directories. To prevent
this introduce a new per-connection mutex: fuse_conn->inst_mutex,
which is taken for instantiations with a directory inode.
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
---
Index: linux/fs/fuse/dir.c
===================================================================
--- linux.orig/fs/fuse/dir.c 2006-10-16 16:17:31.000000000 +0200
+++ linux/fs/fuse/dir.c 2006-10-16 16:21:11.000000000 +0200
@@ -177,22 +177,6 @@ static int fuse_dentry_revalidate(struct
return 1;
}
-/*
- * Check if there's already a hashed alias of this directory inode.
- * If yes, then lookup and mkdir must not create a new alias.
- */
-static int dir_alias(struct inode *inode)
-{
- if (S_ISDIR(inode->i_mode)) {
- struct dentry *alias = d_find_alias(inode);
- if (alias) {
- dput(alias);
- return 1;
- }
- }
- return 0;
-}
-
static int invalid_nodeid(u64 nodeid)
{
return !nodeid || nodeid == FUSE_ROOT_ID;
@@ -208,6 +192,24 @@ static int valid_mode(int m)
S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(m);
}
+/*
+ * Add a directory inode to a dentry, ensuring that no other dentry
+ * refers to this inode. Called with fc->inst_mutex.
+ */
+static int fuse_d_add_directory(struct dentry *entry, struct inode *inode)
+{
+ struct dentry *alias = d_find_alias(inode);
+ if (alias) {
+ /* This tries to shrink the subtree below alias */
+ fuse_invalidate_entry(alias);
+ dput(alias);
+ if (!list_empty(&inode->i_dentry))
+ return -EBUSY;
+ }
+ d_add(entry, inode);
+ return 0;
+}
+
static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
struct nameidata *nd)
{
@@ -243,11 +245,17 @@ static struct dentry *fuse_lookup(struct
if (err && err != -ENOENT)
return ERR_PTR(err);
- if (inode && dir_alias(inode)) {
- iput(inode);
- return ERR_PTR(-EIO);
- }
- d_add(entry, inode);
+ if (inode && S_ISDIR(inode->i_mode)) {
+ mutex_lock(&fc->inst_mutex);
+ err = fuse_d_add_directory(entry, inode);
+ mutex_unlock(&fc->inst_mutex);
+ if (err) {
+ iput(inode);
+ return ERR_PTR(err);
+ }
+ } else
+ d_add(entry, inode);
+
entry->d_op = &fuse_dentry_operations;
if (!err)
fuse_change_timeout(entry, &outarg);
@@ -403,12 +411,22 @@ static int create_new_entry(struct fuse_
}
fuse_put_request(fc, req);
- if (dir_alias(inode)) {
- iput(inode);
- return -EIO;
- }
+ if (S_ISDIR(inode->i_mode)) {
+ struct dentry *alias;
+ mutex_lock(&fc->inst_mutex);
+ alias = d_find_alias(inode);
+ if (alias) {
+ /* New directory must have moved since mkdir */
+ mutex_unlock(&fc->inst_mutex);
+ dput(alias);
+ iput(inode);
+ return -EBUSY;
+ }
+ d_instantiate(entry, inode);
+ mutex_unlock(&fc->inst_mutex);
+ } else
+ d_instantiate(entry, inode);
- d_instantiate(entry, inode);
fuse_change_timeout(entry, &outarg);
fuse_invalidate_attr(dir);
return 0;
Index: linux/fs/fuse/fuse_i.h
===================================================================
--- linux.orig/fs/fuse/fuse_i.h 2006-10-16 16:17:31.000000000 +0200
+++ linux/fs/fuse/fuse_i.h 2006-10-16 16:17:36.000000000 +0200
@@ -239,6 +239,9 @@ struct fuse_conn {
/** Lock protecting accessess to members of this structure */
spinlock_t lock;
+ /** Mutex protecting against directory alias creation */
+ struct mutex inst_mutex;
+
/** Refcount */
atomic_t count;
Index: linux/fs/fuse/inode.c
===================================================================
--- linux.orig/fs/fuse/inode.c 2006-10-16 16:17:31.000000000 +0200
+++ linux/fs/fuse/inode.c 2006-10-16 16:17:36.000000000 +0200
@@ -379,6 +379,7 @@ static struct fuse_conn *new_conn(void)
fc = kzalloc(sizeof(*fc), GFP_KERNEL);
if (fc) {
spin_lock_init(&fc->lock);
+ mutex_init(&fc->inst_mutex);
atomic_set(&fc->count, 1);
init_waitqueue_head(&fc->waitq);
init_waitqueue_head(&fc->blocked_waitq);
@@ -398,8 +399,10 @@ static struct fuse_conn *new_conn(void)
void fuse_conn_put(struct fuse_conn *fc)
{
- if (atomic_dec_and_test(&fc->count))
+ if (atomic_dec_and_test(&fc->count)) {
+ mutex_destroy(&fc->inst_mutex);
kfree(fc);
+ }
}
struct fuse_conn *fuse_conn_get(struct fuse_conn *fc)
--
^ permalink raw reply [flat|nested] 16+ messages in thread
* [patch 06/12] fuse: fix dereferencing dentry parent
2006-10-16 16:27 [patch 00/12] fuse update Miklos Szeredi
` (4 preceding siblings ...)
2006-10-16 16:27 ` [patch 05/12] fuse: fix handling of moved directory Miklos Szeredi
@ 2006-10-16 16:27 ` Miklos Szeredi
2006-10-16 16:27 ` [patch 07/12] fuse: update userspace interface to version 7.8 Miklos Szeredi
` (5 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Miklos Szeredi @ 2006-10-16 16:27 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel
[-- Attachment #1: fuse_parent_deref_fix.patch --]
[-- Type: text/plain, Size: 1240 bytes --]
There's no locking for ->d_revalidate, so fuse_dentry_revalidate()
should use dget_parent() instead of simply dereferencing ->d_parent.
Due to topology changes in the directory tree the parent could become
negative or be destroyed while being used. There hasn't been any
reports about this yet.
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
---
Index: linux/fs/fuse/dir.c
===================================================================
--- linux.orig/fs/fuse/dir.c 2006-10-16 16:21:11.000000000 +0200
+++ linux/fs/fuse/dir.c 2006-10-16 16:21:20.000000000 +0200
@@ -138,6 +138,7 @@ static int fuse_dentry_revalidate(struct
struct fuse_entry_out outarg;
struct fuse_conn *fc;
struct fuse_req *req;
+ struct dentry *parent;
/* Doesn't hurt to "reset" the validity timeout */
fuse_invalidate_entry_cache(entry);
@@ -151,8 +152,10 @@ static int fuse_dentry_revalidate(struct
if (IS_ERR(req))
return 0;
- fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg);
+ parent = dget_parent(entry);
+ fuse_lookup_init(req, parent->d_inode, entry, &outarg);
request_send(fc, req);
+ dput(parent);
err = req->out.h.error;
/* Zero nodeid is same as -ENOENT */
if (!err && !outarg.nodeid)
--
^ permalink raw reply [flat|nested] 16+ messages in thread
* [patch 07/12] fuse: update userspace interface to version 7.8
2006-10-16 16:27 [patch 00/12] fuse update Miklos Szeredi
` (5 preceding siblings ...)
2006-10-16 16:27 ` [patch 06/12] fuse: fix dereferencing dentry parent Miklos Szeredi
@ 2006-10-16 16:27 ` Miklos Szeredi
2006-10-16 16:27 ` [patch 08/12] fuse: minor cleanup in fuse_dentry_revalidate Miklos Szeredi
` (4 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Miklos Szeredi @ 2006-10-16 16:27 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel
[-- Attachment #1: fuse_interface_update.patch --]
[-- Type: text/plain, Size: 1366 bytes --]
Add a flag to the RELEASE message which specifies that a FLUSH
operation should be performed as well. This interface update is
needed for the FreeBSD port, and doesn't actually touch the Linux
implementation at all.
Also rename the unused 'flush_flags' in the FLUSH message to 'unused'.
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
---
Index: linux/include/linux/fuse.h
===================================================================
--- linux.orig/include/linux/fuse.h 2006-10-16 16:17:31.000000000 +0200
+++ linux/include/linux/fuse.h 2006-10-16 16:21:26.000000000 +0200
@@ -15,7 +15,7 @@
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 7
+#define FUSE_KERNEL_MINOR_VERSION 8
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
@@ -92,6 +92,11 @@ struct fuse_file_lock {
#define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1)
+/**
+ * Release flags
+ */
+#define FUSE_RELEASE_FLUSH (1 << 0)
+
enum fuse_opcode {
FUSE_LOOKUP = 1,
FUSE_FORGET = 2, /* no reply */
@@ -205,12 +210,13 @@ struct fuse_open_out {
struct fuse_release_in {
__u64 fh;
__u32 flags;
- __u32 padding;
+ __u32 release_flags;
+ __u64 lock_owner;
};
struct fuse_flush_in {
__u64 fh;
- __u32 flush_flags;
+ __u32 unused;
__u32 padding;
__u64 lock_owner;
};
--
^ permalink raw reply [flat|nested] 16+ messages in thread
* [patch 08/12] fuse: minor cleanup in fuse_dentry_revalidate
2006-10-16 16:27 [patch 00/12] fuse update Miklos Szeredi
` (6 preceding siblings ...)
2006-10-16 16:27 ` [patch 07/12] fuse: update userspace interface to version 7.8 Miklos Szeredi
@ 2006-10-16 16:27 ` Miklos Szeredi
2006-10-16 16:27 ` [patch 09/12] fuse: add support for block device based filesystems Miklos Szeredi
` (3 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Miklos Szeredi @ 2006-10-16 16:27 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel
[-- Attachment #1: fuse_dentry_revalidate_cleanup.patch --]
[-- Type: text/plain, Size: 727 bytes --]
Remove unneeded code from fuse_dentry_revalidate(). This made some
sense while the validity time could wrap around, but now it's a very
obvious no-op.
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
---
Index: linux/fs/fuse/dir.c
===================================================================
--- linux.orig/fs/fuse/dir.c 2006-10-16 16:21:20.000000000 +0200
+++ linux/fs/fuse/dir.c 2006-10-16 16:21:29.000000000 +0200
@@ -140,9 +140,6 @@ static int fuse_dentry_revalidate(struct
struct fuse_req *req;
struct dentry *parent;
- /* Doesn't hurt to "reset" the validity timeout */
- fuse_invalidate_entry_cache(entry);
-
/* For negative dentries, always do a fresh lookup */
if (!inode)
return 0;
--
^ permalink raw reply [flat|nested] 16+ messages in thread
* [patch 09/12] fuse: add support for block device based filesystems
2006-10-16 16:27 [patch 00/12] fuse update Miklos Szeredi
` (7 preceding siblings ...)
2006-10-16 16:27 ` [patch 08/12] fuse: minor cleanup in fuse_dentry_revalidate Miklos Szeredi
@ 2006-10-16 16:27 ` Miklos Szeredi
2006-10-16 16:27 ` [patch 10/12] fuse: add blksize option Miklos Szeredi
` (2 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Miklos Szeredi @ 2006-10-16 16:27 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel
[-- Attachment #1: fuse_blockdev.patch --]
[-- Type: text/plain, Size: 3596 bytes --]
I never intended this, but people started using fuse to implement
block device based "real" filesystems (ntfs-3g, zfs).
The following four patches add better support for these kinds of
filesystems. Unlike "normal" fuse filesystems, using this feature
should require superuser privileges (enforced by the fusermount
utility).
Thanks to Szabolcs Szakacsits for the input and testing.
This patch adds a 'fuseblk' filesystem type, which is only different
from the 'fuse' filesystem type in how the 'dev_name' mount argument
is interpreted.
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
---
Index: linux/Documentation/filesystems/fuse.txt
===================================================================
--- linux.orig/Documentation/filesystems/fuse.txt 2006-10-16 16:17:30.000000000 +0200
+++ linux/Documentation/filesystems/fuse.txt 2006-10-16 16:21:33.000000000 +0200
@@ -51,6 +51,22 @@ homepage:
http://fuse.sourceforge.net/
+Filesystem type
+~~~~~~~~~~~~~~~
+
+The filesystem type given to mount(2) can be one of the following:
+
+'fuse'
+
+ This is the usual way to mount a FUSE filesystem. The first
+ argument of the mount system call may contain an arbitrary string,
+ which is not interpreted by the kernel.
+
+'fuseblk'
+
+ The filesystem is block device based. The first argument of the
+ mount system call is interpreted as the name of the device.
+
Mount options
~~~~~~~~~~~~~
Index: linux/fs/fuse/inode.c
===================================================================
--- linux.orig/fs/fuse/inode.c 2006-10-16 16:17:36.000000000 +0200
+++ linux/fs/fuse/inode.c 2006-10-16 16:21:33.000000000 +0200
@@ -591,6 +591,14 @@ static int fuse_get_sb(struct file_syste
return get_sb_nodev(fs_type, flags, raw_data, fuse_fill_super, mnt);
}
+static int fuse_get_sb_blk(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *raw_data, struct vfsmount *mnt)
+{
+ return get_sb_bdev(fs_type, flags, dev_name, raw_data, fuse_fill_super,
+ mnt);
+}
+
static struct file_system_type fuse_fs_type = {
.owner = THIS_MODULE,
.name = "fuse",
@@ -598,6 +606,14 @@ static struct file_system_type fuse_fs_t
.kill_sb = kill_anon_super,
};
+static struct file_system_type fuseblk_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "fuseblk",
+ .get_sb = fuse_get_sb_blk,
+ .kill_sb = kill_block_super,
+ .fs_flags = FS_REQUIRES_DEV,
+};
+
static decl_subsys(fuse, NULL, NULL);
static decl_subsys(connections, NULL, NULL);
@@ -617,24 +633,34 @@ static int __init fuse_fs_init(void)
err = register_filesystem(&fuse_fs_type);
if (err)
- printk("fuse: failed to register filesystem\n");
- else {
- fuse_inode_cachep = kmem_cache_create("fuse_inode",
- sizeof(struct fuse_inode),
- 0, SLAB_HWCACHE_ALIGN,
- fuse_inode_init_once, NULL);
- if (!fuse_inode_cachep) {
- unregister_filesystem(&fuse_fs_type);
- err = -ENOMEM;
- }
- }
+ goto out;
+ err = register_filesystem(&fuseblk_fs_type);
+ if (err)
+ goto out_unreg;
+
+ fuse_inode_cachep = kmem_cache_create("fuse_inode",
+ sizeof(struct fuse_inode),
+ 0, SLAB_HWCACHE_ALIGN,
+ fuse_inode_init_once, NULL);
+ err = -ENOMEM;
+ if (!fuse_inode_cachep)
+ goto out_unreg2;
+
+ return 0;
+
+ out_unreg2:
+ unregister_filesystem(&fuseblk_fs_type);
+ out_unreg:
+ unregister_filesystem(&fuse_fs_type);
+ out:
return err;
}
static void fuse_fs_cleanup(void)
{
unregister_filesystem(&fuse_fs_type);
+ unregister_filesystem(&fuseblk_fs_type);
kmem_cache_destroy(fuse_inode_cachep);
}
--
^ permalink raw reply [flat|nested] 16+ messages in thread
* [patch 10/12] fuse: add blksize option
2006-10-16 16:27 [patch 00/12] fuse update Miklos Szeredi
` (8 preceding siblings ...)
2006-10-16 16:27 ` [patch 09/12] fuse: add support for block device based filesystems Miklos Szeredi
@ 2006-10-16 16:27 ` Miklos Szeredi
2006-10-16 16:27 ` [patch 11/12] fuse: add bmap support Miklos Szeredi
2006-10-16 16:27 ` [patch 12/12] fuse: add DESTROY operation Miklos Szeredi
11 siblings, 0 replies; 16+ messages in thread
From: Miklos Szeredi @ 2006-10-16 16:27 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel
[-- Attachment #1: fuse_blocksize.patch --]
[-- Type: text/plain, Size: 2920 bytes --]
Add 'blksize' option for block device based filesystems. During
initialization this is used to set the block size on the device and
the super block. The default block size is 512bytes.
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
---
Index: linux/Documentation/filesystems/fuse.txt
===================================================================
--- linux.orig/Documentation/filesystems/fuse.txt 2006-10-16 16:21:33.000000000 +0200
+++ linux/Documentation/filesystems/fuse.txt 2006-10-16 16:21:38.000000000 +0200
@@ -110,6 +110,11 @@ Mount options
The default is infinite. Note that the size of read requests is
limited anyway to 32 pages (which is 128kbyte on i386).
+'blksize=N'
+
+ Set the block size for the filesystem. The default is 512. This
+ option is only valid for 'fuseblk' type mounts.
+
Control filesystem
~~~~~~~~~~~~~~~~~~
Index: linux/fs/fuse/inode.c
===================================================================
--- linux.orig/fs/fuse/inode.c 2006-10-16 16:21:33.000000000 +0200
+++ linux/fs/fuse/inode.c 2006-10-16 16:21:38.000000000 +0200
@@ -39,6 +39,7 @@ struct fuse_mount_data {
unsigned group_id_present : 1;
unsigned flags;
unsigned max_read;
+ unsigned blksize;
};
static struct inode *fuse_alloc_inode(struct super_block *sb)
@@ -274,6 +275,7 @@ enum {
OPT_DEFAULT_PERMISSIONS,
OPT_ALLOW_OTHER,
OPT_MAX_READ,
+ OPT_BLKSIZE,
OPT_ERR
};
@@ -285,14 +287,16 @@ static match_table_t tokens = {
{OPT_DEFAULT_PERMISSIONS, "default_permissions"},
{OPT_ALLOW_OTHER, "allow_other"},
{OPT_MAX_READ, "max_read=%u"},
+ {OPT_BLKSIZE, "blksize=%u"},
{OPT_ERR, NULL}
};
-static int parse_fuse_opt(char *opt, struct fuse_mount_data *d)
+static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev)
{
char *p;
memset(d, 0, sizeof(struct fuse_mount_data));
d->max_read = ~0;
+ d->blksize = 512;
while ((p = strsep(&opt, ",")) != NULL) {
int token;
@@ -345,6 +349,12 @@ static int parse_fuse_opt(char *opt, str
d->max_read = value;
break;
+ case OPT_BLKSIZE:
+ if (!is_bdev || match_int(&args[0], &value))
+ return 0;
+ d->blksize = value;
+ break;
+
default:
return 0;
}
@@ -500,15 +510,21 @@ static int fuse_fill_super(struct super_
struct dentry *root_dentry;
struct fuse_req *init_req;
int err;
+ int is_bdev = sb->s_bdev != NULL;
if (sb->s_flags & MS_MANDLOCK)
return -EINVAL;
- if (!parse_fuse_opt((char *) data, &d))
+ if (!parse_fuse_opt((char *) data, &d, is_bdev))
return -EINVAL;
- sb->s_blocksize = PAGE_CACHE_SIZE;
- sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ if (is_bdev) {
+ if (!sb_set_blocksize(sb, d.blksize))
+ return -EINVAL;
+ } else {
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ }
sb->s_magic = FUSE_SUPER_MAGIC;
sb->s_op = &fuse_super_operations;
sb->s_maxbytes = MAX_LFS_FILESIZE;
--
^ permalink raw reply [flat|nested] 16+ messages in thread
* [patch 11/12] fuse: add bmap support
2006-10-16 16:27 [patch 00/12] fuse update Miklos Szeredi
` (9 preceding siblings ...)
2006-10-16 16:27 ` [patch 10/12] fuse: add blksize option Miklos Szeredi
@ 2006-10-16 16:27 ` Miklos Szeredi
2006-10-16 16:27 ` [patch 12/12] fuse: add DESTROY operation Miklos Szeredi
11 siblings, 0 replies; 16+ messages in thread
From: Miklos Szeredi @ 2006-10-16 16:27 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel
[-- Attachment #1: fuse_bmap.patch --]
[-- Type: text/plain, Size: 3547 bytes --]
Add support for the BMAP operation for block device based filesystems.
This is needed to support swap-files and lilo.
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
---
Index: linux/fs/fuse/file.c
===================================================================
--- linux.orig/fs/fuse/file.c 2006-10-16 16:17:30.000000000 +0200
+++ linux/fs/fuse/file.c 2006-10-16 16:21:39.000000000 +0200
@@ -757,6 +757,42 @@ static int fuse_file_lock(struct file *f
return err;
}
+static sector_t fuse_bmap(struct address_space *mapping, sector_t block)
+{
+ struct inode *inode = mapping->host;
+ struct fuse_conn *fc = get_fuse_conn(inode);
+ struct fuse_req *req;
+ struct fuse_bmap_in inarg;
+ struct fuse_bmap_out outarg;
+ int err;
+
+ if (!inode->i_sb->s_bdev || fc->no_bmap)
+ return 0;
+
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return 0;
+
+ memset(&inarg, 0, sizeof(inarg));
+ inarg.block = block;
+ inarg.blocksize = inode->i_sb->s_blocksize;
+ req->in.h.opcode = FUSE_BMAP;
+ req->in.h.nodeid = get_node_id(inode);
+ req->in.numargs = 1;
+ req->in.args[0].size = sizeof(inarg);
+ req->in.args[0].value = &inarg;
+ req->out.numargs = 1;
+ req->out.args[0].size = sizeof(outarg);
+ req->out.args[0].value = &outarg;
+ request_send(fc, req);
+ err = req->out.h.error;
+ fuse_put_request(fc, req);
+ if (err == -ENOSYS)
+ fc->no_bmap = 1;
+
+ return err ? 0 : outarg.block;
+}
+
static const struct file_operations fuse_file_operations = {
.llseek = generic_file_llseek,
.read = do_sync_read,
@@ -790,6 +826,7 @@ static const struct address_space_operat
.commit_write = fuse_commit_write,
.readpages = fuse_readpages,
.set_page_dirty = fuse_set_page_dirty,
+ .bmap = fuse_bmap,
};
void fuse_init_file_inode(struct inode *inode)
Index: linux/fs/fuse/fuse_i.h
===================================================================
--- linux.orig/fs/fuse/fuse_i.h 2006-10-16 16:17:36.000000000 +0200
+++ linux/fs/fuse/fuse_i.h 2006-10-16 16:21:39.000000000 +0200
@@ -339,6 +339,9 @@ struct fuse_conn {
/** Is interrupt not implemented by fs? */
unsigned no_interrupt : 1;
+ /** Is bmap not implemented by fs? */
+ unsigned no_bmap : 1;
+
/** The number of requests waiting for completion */
atomic_t num_waiting;
Index: linux/include/linux/fuse.h
===================================================================
--- linux.orig/include/linux/fuse.h 2006-10-16 16:21:26.000000000 +0200
+++ linux/include/linux/fuse.h 2006-10-16 16:21:39.000000000 +0200
@@ -132,6 +132,7 @@ enum fuse_opcode {
FUSE_ACCESS = 34,
FUSE_CREATE = 35,
FUSE_INTERRUPT = 36,
+ FUSE_BMAP = 37,
};
/* The read buffer is required to be at least 8k, but may be much larger */
@@ -302,6 +303,16 @@ struct fuse_interrupt_in {
__u64 unique;
};
+struct fuse_bmap_in {
+ __u64 block;
+ __u32 blocksize;
+ __u32 padding;
+};
+
+struct fuse_bmap_out {
+ __u64 block;
+};
+
struct fuse_in_header {
__u32 len;
__u32 opcode;
Index: linux/fs/fuse/dir.c
===================================================================
--- linux.orig/fs/fuse/dir.c 2006-10-16 16:21:29.000000000 +0200
+++ linux/fs/fuse/dir.c 2006-10-16 16:21:39.000000000 +0200
@@ -1000,6 +1000,8 @@ static int fuse_setattr(struct dentry *e
if (attr->ia_valid & ATTR_SIZE) {
unsigned long limit;
is_truncate = 1;
+ if (IS_SWAPFILE(inode))
+ return -ETXTBSY;
limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
if (limit != RLIM_INFINITY && attr->ia_size > (loff_t) limit) {
send_sig(SIGXFSZ, current, 0);
--
^ permalink raw reply [flat|nested] 16+ messages in thread
* [patch 12/12] fuse: add DESTROY operation
2006-10-16 16:27 [patch 00/12] fuse update Miklos Szeredi
` (10 preceding siblings ...)
2006-10-16 16:27 ` [patch 11/12] fuse: add bmap support Miklos Szeredi
@ 2006-10-16 16:27 ` Miklos Szeredi
11 siblings, 0 replies; 16+ messages in thread
From: Miklos Szeredi @ 2006-10-16 16:27 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel
[-- Attachment #1: fuse_destroy_method.patch --]
[-- Type: text/plain, Size: 3293 bytes --]
Add a DESTROY operation for block device based filesystems. With the
help of this operation, such a filesystem can flush dirty data to the
device synchronously before the umount returns.
This is needed in situations where the filesystem is assumed to be
clean immediately after unmount (e.g. ejecting removable media).
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
---
Index: linux/fs/fuse/fuse_i.h
===================================================================
--- linux.orig/fs/fuse/fuse_i.h 2006-10-16 16:21:39.000000000 +0200
+++ linux/fs/fuse/fuse_i.h 2006-10-16 16:21:43.000000000 +0200
@@ -298,6 +298,9 @@ struct fuse_conn {
reply, before any other request, and never cleared */
unsigned conn_error : 1;
+ /** Connection successful. Only set in INIT */
+ unsigned conn_init : 1;
+
/** Do readpages asynchronously? Only set in INIT */
unsigned async_read : 1;
@@ -368,6 +371,9 @@ struct fuse_conn {
/** Key for lock owner ID scrambling */
u32 scramble_key[4];
+
+ /** Reserved request for the DESTROY message */
+ struct fuse_req *destroy_req;
};
static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb)
Index: linux/fs/fuse/inode.c
===================================================================
--- linux.orig/fs/fuse/inode.c 2006-10-16 16:21:38.000000000 +0200
+++ linux/fs/fuse/inode.c 2006-10-16 16:21:43.000000000 +0200
@@ -206,10 +206,23 @@ static void fuse_umount_begin(struct vfs
fuse_abort_conn(get_fuse_conn_super(vfsmnt->mnt_sb));
}
+static void fuse_send_destroy(struct fuse_conn *fc)
+{
+ struct fuse_req *req = fc->destroy_req;
+ if (req && fc->conn_init) {
+ fc->destroy_req = NULL;
+ req->in.h.opcode = FUSE_DESTROY;
+ req->force = 1;
+ request_send(fc, req);
+ fuse_put_request(fc, req);
+ }
+}
+
static void fuse_put_super(struct super_block *sb)
{
struct fuse_conn *fc = get_fuse_conn_super(sb);
+ fuse_send_destroy(fc);
spin_lock(&fc->lock);
fc->connected = 0;
fc->blocked = 0;
@@ -410,6 +423,8 @@ static struct fuse_conn *new_conn(void)
void fuse_conn_put(struct fuse_conn *fc)
{
if (atomic_dec_and_test(&fc->count)) {
+ if (fc->destroy_req)
+ fuse_request_free(fc->destroy_req);
mutex_destroy(&fc->inst_mutex);
kfree(fc);
}
@@ -466,6 +481,7 @@ static void process_init_reply(struct fu
fc->bdi.ra_pages = min(fc->bdi.ra_pages, ra_pages);
fc->minor = arg->minor;
fc->max_write = arg->minor < 5 ? 4096 : arg->max_write;
+ fc->conn_init = 1;
}
fuse_put_request(fc, req);
fc->blocked = 0;
@@ -563,6 +579,12 @@ static int fuse_fill_super(struct super_
if (!init_req)
goto err_put_root;
+ if (is_bdev) {
+ fc->destroy_req = fuse_request_alloc();
+ if (!fc->destroy_req)
+ goto err_put_root;
+ }
+
mutex_lock(&fuse_mutex);
err = -EINVAL;
if (file->private_data)
Index: linux/include/linux/fuse.h
===================================================================
--- linux.orig/include/linux/fuse.h 2006-10-16 16:21:39.000000000 +0200
+++ linux/include/linux/fuse.h 2006-10-16 16:21:43.000000000 +0200
@@ -133,6 +133,7 @@ enum fuse_opcode {
FUSE_CREATE = 35,
FUSE_INTERRUPT = 36,
FUSE_BMAP = 37,
+ FUSE_DESTROY = 38,
};
/* The read buffer is required to be at least 8k, but may be much larger */
--
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [patch 01/12] fuse: fix hang on SMP
2006-10-16 16:27 ` [patch 01/12] fuse: fix hang on SMP Miklos Szeredi
@ 2006-10-16 23:51 ` Andrew Morton
2006-10-17 12:53 ` Mike Day
0 siblings, 1 reply; 16+ messages in thread
From: Andrew Morton @ 2006-10-16 23:51 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: linux-kernel, linux-fsdevel
On Mon, 16 Oct 2006 18:27:10 +0200
Miklos Szeredi <miklos@szeredi.hu> wrote:
> Fuse didn't always call i_size_write() with i_mutex held which caused
> rare hangs on SMP/32bit.
Yes, that is a bit of a trap. I'll maintain a patch in -mm which spits a
warning if i_size_write() is called without i_mutex held.
--- a/include/linux/fs.h~mm-only-i_size_write-debugging
+++ a/include/linux/fs.h
@@ -646,25 +646,7 @@ static inline loff_t i_size_read(struct
#endif
}
-/*
- * NOTE: unlike i_size_read(), i_size_write() does need locking around it
- * (normally i_mutex), otherwise on 32bit/SMP an update of i_size_seqcount
- * can be lost, resulting in subsequent i_size_read() calls spinning forever.
- */
-static inline void i_size_write(struct inode *inode, loff_t i_size)
-{
-#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
- write_seqcount_begin(&inode->i_size_seqcount);
- inode->i_size = i_size;
- write_seqcount_end(&inode->i_size_seqcount);
-#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPT)
- preempt_disable();
- inode->i_size = i_size;
- preempt_enable();
-#else
- inode->i_size = i_size;
-#endif
-}
+void i_size_write(struct inode *inode, loff_t i_size);
static inline unsigned iminor(struct inode *inode)
{
diff -puN fs/inode.c~mm-only-i_size_write-debugging fs/inode.c
--- a/fs/inode.c~mm-only-i_size_write-debugging
+++ a/fs/inode.c
@@ -1384,6 +1384,23 @@ void __init inode_init(unsigned long mem
INIT_HLIST_HEAD(&inode_hashtable[loop]);
}
+void i_size_write(struct inode *inode, loff_t i_size)
+{
+ WARN_ON_ONCE(!mutex_is_locked(&inode->i_mutex));
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
+ write_seqcount_begin(&inode->i_size_seqcount);
+ inode->i_size = i_size;
+ write_seqcount_end(&inode->i_size_seqcount);
+#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPT)
+ preempt_disable();
+ inode->i_size = i_size;
+ preempt_enable();
+#else
+ inode->i_size = i_size;
+#endif
+}
+EXPORT_SYMBOL(i_size_write);
+
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
inode->i_mode = mode;
_
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: fuse: fix hang on SMP
2006-10-16 23:51 ` Andrew Morton
@ 2006-10-17 12:53 ` Mike Day
2006-10-17 13:43 ` Miklos Szeredi
0 siblings, 1 reply; 16+ messages in thread
From: Mike Day @ 2006-10-17 12:53 UTC (permalink / raw)
To: Andrew Morton; +Cc: Miklos Szeredi, linux-kernel, linux-fsdevel
[-- Attachment #1: Type: text/plain, Size: 1087 bytes --]
On 16/10/06 16:51 -0700, Andrew Morton wrote:
>On Mon, 16 Oct 2006 18:27:10 +0200
>Miklos Szeredi <miklos@szeredi.hu> wrote:
>
>> Fuse didn't always call i_size_write() with i_mutex held which caused
>> rare hangs on SMP/32bit.
>
>Yes, that is a bit of a trap. I'll maintain a patch in -mm which spits a
>warning if i_size_write() is called without i_mutex held.
>
>+void i_size_write(struct inode *inode, loff_t i_size)
>+{
>+ WARN_ON_ONCE(!mutex_is_locked(&inode->i_mutex));
Miklos' patch would generate this warning because he uses the spinlock
inside struct fuse_conn to synchronize the write:
+static void fuse_vmtruncate(struct inode *inode, loff_t offset)
+{
+ struct fuse_conn *fc = get_fuse_conn(inode);
+ int need_trunc;
+
+ spin_lock(&fc->lock);
+ need_trunc = inode->i_size > offset;
+ i_size_write(inode, offset);
+ spin_unlock(&fc->lock);
--
Mike D. Day
IBM LTC
Cell: 919 412-3900
Sametime: ncmike@us.ibm.com AIM: ncmikeday Yahoo: ultra.runner
PGP key: http://www.ncultra.org/ncmike/pubkey.asc
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 191 bytes --]
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: fuse: fix hang on SMP
2006-10-17 12:53 ` Mike Day
@ 2006-10-17 13:43 ` Miklos Szeredi
0 siblings, 0 replies; 16+ messages in thread
From: Miklos Szeredi @ 2006-10-17 13:43 UTC (permalink / raw)
To: ncmike; +Cc: akpm, linux-kernel, linux-fsdevel
> On 16/10/06 16:51 -0700, Andrew Morton wrote:
> >On Mon, 16 Oct 2006 18:27:10 +0200
> >Miklos Szeredi <miklos@szeredi.hu> wrote:
> >
> >> Fuse didn't always call i_size_write() with i_mutex held which caused
> >> rare hangs on SMP/32bit.
> >
> >Yes, that is a bit of a trap. I'll maintain a patch in -mm which spits a
> >warning if i_size_write() is called without i_mutex held.
> >
>
> >+void i_size_write(struct inode *inode, loff_t i_size)
> >+{
> >+ WARN_ON_ONCE(!mutex_is_locked(&inode->i_mutex));
>
>
> Miklos' patch would generate this warning because he uses the spinlock
> inside struct fuse_conn to synchronize the write:
Yes, this will cause a false alarm for FUSE, but it may still be
useful to find similar bugs in other filesystems.
Miklos
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2006-10-17 13:43 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-10-16 16:27 [patch 00/12] fuse update Miklos Szeredi
2006-10-16 16:27 ` [patch 01/12] fuse: fix hang on SMP Miklos Szeredi
2006-10-16 23:51 ` Andrew Morton
2006-10-17 12:53 ` Mike Day
2006-10-17 13:43 ` Miklos Szeredi
2006-10-16 16:27 ` [patch 02/12] document i_size_write locking rules Miklos Szeredi
2006-10-16 16:27 ` [patch 03/12] fuse: locking fix for nlookup Miklos Szeredi
2006-10-16 16:27 ` [patch 04/12] fuse: fix spurious BUG Miklos Szeredi
2006-10-16 16:27 ` [patch 05/12] fuse: fix handling of moved directory Miklos Szeredi
2006-10-16 16:27 ` [patch 06/12] fuse: fix dereferencing dentry parent Miklos Szeredi
2006-10-16 16:27 ` [patch 07/12] fuse: update userspace interface to version 7.8 Miklos Szeredi
2006-10-16 16:27 ` [patch 08/12] fuse: minor cleanup in fuse_dentry_revalidate Miklos Szeredi
2006-10-16 16:27 ` [patch 09/12] fuse: add support for block device based filesystems Miklos Szeredi
2006-10-16 16:27 ` [patch 10/12] fuse: add blksize option Miklos Szeredi
2006-10-16 16:27 ` [patch 11/12] fuse: add bmap support Miklos Szeredi
2006-10-16 16:27 ` [patch 12/12] fuse: add DESTROY operation Miklos Szeredi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).