fix hang, etc
authorMiklos Szeredi <miklos@szeredi.hu>
Fri, 13 Oct 2006 19:48:19 +0000 (19:48 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Fri, 13 Oct 2006 19:48:19 +0000 (19:48 +0000)
ChangeLog
kernel/dir.c
kernel/file.c
kernel/inode.c
lib/fuse.c

index 822cff9b7ddb7ca2f056b9a71cf4f10f9522fb72..d2098945cdc03aef8dc268953519b96d2aa07135 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,8 +1,23 @@
 2006-10-13  Miklos Szeredi <miklos@szeredi.hu>
 
-       * kernel: Fix compilation on patched 2.6.18 (fc6) and 2.6.9.
+       * kernel: Fix compilation on patched 2.6.18 (fc6) and 2.6.19.
        Report from David Shaw
 
+       * lib: Fix lost error on renaming a file.  Introduced in version
+       2.4.1. Report from David Shaw
+
+       * kernel: Fix a rare hang on SMP/32bit on heavy filesystem
+       activity.  The cause of the bug was that some calls to
+       i_size_write() were not protected by a lock, and hence
+       i_size_seqcount could become corrupted.  This caused subsequent
+       calls to i_size_read() to spin forever.  This is a long standing
+       bug was probably introduced in version 2.2, and thought to be
+       related to NFS exporting (it's not).  It was reported by various
+       people, but Dana Henriksen has finally helped me to track it down,
+       so big thanks to him
+
+       * kernel: Protect against truncation of a swapfile
+
 2006-10-10  Miklos Szeredi <miklos@szeredi.hu>
 
        * kernel: Check for signature of super_operations->umount_begin().
index 3f12e19c4f5b47d1d6eb453396396f2f671c3092..b45d5140cb84724ec5751656981c9b3083a0cdba 100644 (file)
@@ -992,14 +992,29 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg)
 #endif
 }
 
+static void fuse_vmtruncate(struct inode *inode, loff_t offset)
+{
+       struct fuse_conn *fc = get_fuse_conn(inode);
+       loff_t origsize = i_size_read(inode);
+       if (origsize != offset) {
+               spin_lock(&fc->lock);
+               i_size_write(inode, offset);
+               spin_unlock(&fc->lock);
+       }
+       if (origsize > offset) {
+               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)
 {
@@ -1030,6 +1045,8 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
                        send_sig(SIGXFSZ, current, 0);
                        return -EFBIG;
                }
+               if (IS_SWAPFILE(inode))
+                       return -ETXTBSY;
        }
 
        req = fuse_get_req(fc);
@@ -1059,12 +1076,8 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
 #endif
                        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 b9d86edec1fcdb106a762d73d7cce2c5bd667111..04700694cf5d01bbe0f7d18664710f31a75d41c8 100644 (file)
@@ -495,8 +495,11 @@ static int fuse_commit_write(struct file *file, struct page *page,
                err = -EIO;
        if (!err) {
                pos += count;
-               if (pos > i_size_read(inode))
+               if (pos > i_size_read(inode)) {
+                       spin_lock(&fc->lock);
                        i_size_write(inode, pos);
+                       spin_unlock(&fc->lock);
+               }
 
                if (offset == 0 && to == PAGE_CACHE_SIZE) {
                        clear_page_dirty(page);
@@ -601,8 +604,11 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
        }
        fuse_put_request(fc, req);
        if (res > 0) {
-               if (write && pos > i_size_read(inode))
+               if (write && pos > i_size_read(inode)) {
+                       spin_lock(&fc->lock);
                        i_size_write(inode, pos);
+                       spin_unlock(&fc->lock);
+               }
                *ppos = pos;
        }
        fuse_invalidate_attr(inode);
index e3f98dbb6e47fd4304b64b926e52f7351d980c04..d70141b541704ec5cb396a22636a00d85bf2c2e9 100644 (file)
@@ -125,6 +125,7 @@ static int fuse_remount_fs(struct super_block *sb, int *flags, char *data)
 
 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);
 
@@ -133,7 +134,9 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
        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);
 #ifdef HAVE_I_BLKSIZE
        inode->i_blksize = PAGE_CACHE_SIZE;
 #endif
@@ -149,7 +152,7 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
 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);
index 8fc1bac1ae04e6cba0def4664369a55da0bd0039..6de98faef93ac7a8fc5fe4ff3563b0570f99d5da 100644 (file)
@@ -701,8 +701,8 @@ static int hide_node(struct fuse *f, fuse_req_t req, const char *oldpath,
     if (f->op.rename && f->op.unlink) {
         newpath = hidden_name(f, req, dir, oldname, newname, sizeof(newname));
         if (newpath) {
-            int res = fuse_do_rename(f, req, oldpath, newpath);
-            if (res == 0)
+            err = fuse_do_rename(f, req, oldpath, newpath);
+            if (!err)
                 err = rename_node(f, dir, oldname, dir, newname, 1);
             free(newpath);
         }
@@ -1346,7 +1346,7 @@ static void fuse_rename(fuse_req_t req, fuse_ino_t olddir, const char *oldname,
                     is_open(f, newdir, newname))
                     err = hide_node(f, req, newpath, newdir, newname);
                 if (!err) {
-                    fuse_do_rename(f, req, oldpath, newpath);
+                    err = fuse_do_rename(f, req, oldpath, newpath);
                     if (!err)
                         err = rename_node(f, olddir, oldname, newdir, newname, 0);
                 }