]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - fs/exofs/inode.c
exofs: Fix double page_unlock BUG in write_begin/end
[net-next-2.6.git] / fs / exofs / inode.c
index 4bb6ef822e46d8daba3b312afbb4258aab7ea1ee..3eadd97324b140e679f269480b3737823fd008cb 100644 (file)
@@ -32,9 +32,6 @@
  */
 
 #include <linux/slab.h>
-#include <linux/writeback.h>
-#include <linux/buffer_head.h>
-#include <scsi/scsi_device.h>
 
 #include "exofs.h"
 
@@ -57,6 +54,9 @@ struct page_collect {
        unsigned nr_pages;
        unsigned long length;
        loff_t pg_first; /* keep 64bit also in 32-arches */
+       bool read_4_write; /* This means two things: that the read is sync
+                           * And the pages should not be unlocked.
+                           */
 };
 
 static void _pcol_init(struct page_collect *pcol, unsigned expected_pages,
@@ -74,6 +74,7 @@ static void _pcol_init(struct page_collect *pcol, unsigned expected_pages,
        pcol->nr_pages = 0;
        pcol->length = 0;
        pcol->pg_first = -1;
+       pcol->read_4_write = false;
 }
 
 static void _pcol_reset(struct page_collect *pcol)
@@ -350,7 +351,8 @@ static int readpage_strip(void *data, struct page *page)
                if (PageError(page))
                        ClearPageError(page);
 
-               unlock_page(page);
+               if (!pcol->read_4_write)
+                       unlock_page(page);
                EXOFS_DBGMSG("readpage_strip(0x%lx, 0x%lx) empty page,"
                             " splitting\n", inode->i_ino, page->index);
 
@@ -431,6 +433,7 @@ static int _readpage(struct page *page, bool is_sync)
        /* readpage_strip might call read_exec(,is_sync==false) at several
         * places but not if we have a single page.
         */
+       pcol.read_4_write = is_sync;
        ret = readpage_strip(&pcol, page);
        if (ret) {
                EXOFS_ERR("_readpage => %d\n", ret);
@@ -697,6 +700,13 @@ static int exofs_writepage(struct page *page, struct writeback_control *wbc)
        return write_exec(&pcol);
 }
 
+/* i_mutex held using inode->i_size directly */
+static void _write_failed(struct inode *inode, loff_t to)
+{
+       if (to > inode->i_size)
+               truncate_pagecache(inode, to, inode->i_size);
+}
+
 int exofs_write_begin(struct file *file, struct address_space *mapping,
                loff_t pos, unsigned len, unsigned flags,
                struct page **pagep, void **fsdata)
@@ -710,7 +720,7 @@ int exofs_write_begin(struct file *file, struct address_space *mapping,
                                         fsdata);
                if (ret) {
                        EXOFS_DBGMSG("simple_write_begin faild\n");
-                       return ret;
+                       goto out;
                }
 
                page = *pagep;
@@ -725,6 +735,9 @@ int exofs_write_begin(struct file *file, struct address_space *mapping,
                        EXOFS_DBGMSG("__readpage_filler faild\n");
                }
        }
+out:
+       if (unlikely(ret))
+               _write_failed(mapping->host, pos + len);
 
        return ret;
 }
@@ -750,6 +763,10 @@ static int exofs_write_end(struct file *file, struct address_space *mapping,
        int ret;
 
        ret = simple_write_end(file, mapping,pos, len, copied, page, fsdata);
+       if (unlikely(ret))
+               _write_failed(inode, pos + len);
+
+       /* TODO: once simple_write_end marks inode dirty remove */
        if (i_size != inode->i_size)
                mark_inode_dirty(inode);
        return ret;
@@ -759,15 +776,13 @@ static int exofs_releasepage(struct page *page, gfp_t gfp)
 {
        EXOFS_DBGMSG("page 0x%lx\n", page->index);
        WARN_ON(1);
-       return try_to_free_buffers(page);
+       return 0;
 }
 
 static void exofs_invalidatepage(struct page *page, unsigned long offset)
 {
-       EXOFS_DBGMSG("page_has_buffers=>%d\n", page_has_buffers(page));
+       EXOFS_DBGMSG("page 0x%lx offset 0x%lx\n", page->index, offset);
        WARN_ON(1);
-
-       block_invalidatepage(page, offset);
 }
 
 const struct address_space_operations exofs_aops = {
@@ -808,87 +823,55 @@ static inline int exofs_inode_is_fast_symlink(struct inode *inode)
        return S_ISLNK(inode->i_mode) && (oi->i_data[0] != 0);
 }
 
-/*
- * get_block_t - Fill in a buffer_head
- * An OSD takes care of block allocation so we just fake an allocation by
- * putting in the inode's sector_t in the buffer_head.
- * TODO: What about the case of create==0 and @iblock does not exist in the
- * object?
- */
-static int exofs_get_block(struct inode *inode, sector_t iblock,
-                   struct buffer_head *bh_result, int create)
-{
-       map_bh(bh_result, inode->i_sb, iblock);
-       return 0;
-}
-
 const struct osd_attr g_attr_logical_length = ATTR_DEF(
        OSD_APAGE_OBJECT_INFORMATION, OSD_ATTR_OI_LOGICAL_LENGTH, 8);
 
-static int _do_truncate(struct inode *inode)
+static int _do_truncate(struct inode *inode, loff_t newsize)
 {
        struct exofs_i_info *oi = exofs_i(inode);
-       loff_t isize = i_size_read(inode);
        int ret;
 
        inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 
-       nobh_truncate_page(inode->i_mapping, isize, exofs_get_block);
+       ret = exofs_oi_truncate(oi, (u64)newsize);
+       if (likely(!ret))
+               truncate_setsize(inode, newsize);
 
-       ret = exofs_oi_truncate(oi, (u64)isize);
-       EXOFS_DBGMSG("(0x%lx) size=0x%llx\n", inode->i_ino, isize);
+       EXOFS_DBGMSG("(0x%lx) size=0x%llx ret=>%d\n",
+                    inode->i_ino, newsize, ret);
        return ret;
 }
 
 /*
- * Truncate a file to the specified size - all we have to do is set the size
- * attribute.  We make sure the object exists first.
- */
-void exofs_truncate(struct inode *inode)
-{
-       struct exofs_i_info *oi = exofs_i(inode);
-       int ret;
-
-       if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
-            || S_ISLNK(inode->i_mode)))
-               return;
-       if (exofs_inode_is_fast_symlink(inode))
-               return;
-       if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
-               return;
-
-       /* if we are about to truncate an object, and it hasn't been
-        * created yet, wait
-        */
-       if (unlikely(wait_obj_created(oi)))
-               goto fail;
-
-       ret = _do_truncate(inode);
-       if (ret)
-               goto fail;
-
-out:
-       mark_inode_dirty(inode);
-       return;
-fail:
-       make_bad_inode(inode);
-       goto out;
-}
-
-/*
- * Set inode attributes - just call generic functions.
+ * Set inode attributes - update size attribute on OSD if needed,
+ *                        otherwise just call generic functions.
  */
 int exofs_setattr(struct dentry *dentry, struct iattr *iattr)
 {
        struct inode *inode = dentry->d_inode;
        int error;
 
+       /* if we are about to modify an object, and it hasn't been
+        * created yet, wait
+        */
+       error = wait_obj_created(exofs_i(inode));
+       if (unlikely(error))
+               return error;
+
        error = inode_change_ok(inode, iattr);
-       if (error)
+       if (unlikely(error))
                return error;
 
-       error = inode_setattr(inode, iattr);
-       return error;
+       if ((iattr->ia_valid & ATTR_SIZE) &&
+           iattr->ia_size != i_size_read(inode)) {
+               error = _do_truncate(inode, iattr->ia_size);
+               if (unlikely(error))
+                       return error;
+       }
+
+       setattr_copy(inode, iattr);
+       mark_inode_dirty(inode);
+       return 0;
 }
 
 static const struct osd_attr g_attr_inode_file_layout = ATTR_DEF(
@@ -1325,7 +1308,7 @@ static void delete_done(struct exofs_io_state *ios, void *p)
  * from the OSD here.  We make sure the object was created before we try and
  * delete it.
  */
-void exofs_delete_inode(struct inode *inode)
+void exofs_evict_inode(struct inode *inode)
 {
        struct exofs_i_info *oi = exofs_i(inode);
        struct super_block *sb = inode->i_sb;
@@ -1335,30 +1318,27 @@ void exofs_delete_inode(struct inode *inode)
 
        truncate_inode_pages(&inode->i_data, 0);
 
-       if (is_bad_inode(inode))
+       /* TODO: should do better here */
+       if (inode->i_nlink || is_bad_inode(inode))
                goto no_delete;
 
-       mark_inode_dirty(inode);
-       exofs_update_inode(inode, inode_needs_sync(inode));
-
        inode->i_size = 0;
-       if (inode->i_blocks)
-               exofs_truncate(inode);
+       end_writeback(inode);
 
-       clear_inode(inode);
+       /* if we are deleting an obj that hasn't been created yet, wait */
+       if (!obj_created(oi)) {
+               BUG_ON(!obj_2bcreated(oi));
+               wait_event(oi->i_wq, obj_created(oi));
+               /* ignore the error attempt a remove anyway */
+       }
 
+       /* Now Remove the OSD objects */
        ret = exofs_get_io_state(&sbi->layout, &ios);
        if (unlikely(ret)) {
                EXOFS_ERR("%s: exofs_get_io_state failed\n", __func__);
                return;
        }
 
-       /* if we are deleting an obj that hasn't been created yet, wait */
-       if (!obj_created(oi)) {
-               BUG_ON(!obj_2bcreated(oi));
-               wait_event(oi->i_wq, obj_created(oi));
-       }
-
        ios->obj.id = exofs_oi_objno(oi);
        ios->done = delete_done;
        ios->private = sbi;
@@ -1374,5 +1354,5 @@ void exofs_delete_inode(struct inode *inode)
        return;
 
 no_delete:
-       clear_inode(inode);
+       end_writeback(inode);
 }