Merge master.kernel.org:/pub/scm/linux/kernel/git/sfrench/cifs-2.6
[pandora-kernel.git] / fs / buffer.c
index 2066e4c..5e4a90e 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/percpu.h>
 #include <linux/slab.h>
 #include <linux/smp_lock.h>
+#include <linux/capability.h>
 #include <linux/blkdev.h>
 #include <linux/file.h>
 #include <linux/quotaops.h>
@@ -153,14 +154,8 @@ int sync_blockdev(struct block_device *bdev)
 {
        int ret = 0;
 
-       if (bdev) {
-               int err;
-
-               ret = filemap_fdatawrite(bdev->bd_inode->i_mapping);
-               err = filemap_fdatawait(bdev->bd_inode->i_mapping);
-               if (!ret)
-                       ret = err;
-       }
+       if (bdev)
+               ret = filemap_write_and_wait(bdev->bd_inode->i_mapping);
        return ret;
 }
 EXPORT_SYMBOL(sync_blockdev);
@@ -358,11 +353,11 @@ static long do_fsync(unsigned int fd, int datasync)
         * We need to protect against concurrent writers,
         * which could cause livelocks in fsync_buffers_list
         */
-       down(&mapping->host->i_sem);
+       mutex_lock(&mapping->host->i_mutex);
        err = file->f_op->fsync(file, file->f_dentry, datasync);
        if (!ret)
                ret = err;
-       up(&mapping->host->i_sem);
+       mutex_unlock(&mapping->host->i_mutex);
        err = filemap_fdatawait(mapping);
        if (!ret)
                ret = err;
@@ -396,7 +391,7 @@ asmlinkage long sys_fdatasync(unsigned int fd)
  * private_lock is contended then so is mapping->tree_lock).
  */
 static struct buffer_head *
-__find_get_block_slow(struct block_device *bdev, sector_t block, int unused)
+__find_get_block_slow(struct block_device *bdev, sector_t block)
 {
        struct inode *bd_inode = bdev->bd_inode;
        struct address_space *bd_mapping = bd_inode->i_mapping;
@@ -1027,12 +1022,13 @@ try_again:
 
                bh->b_state = 0;
                atomic_set(&bh->b_count, 0);
+               bh->b_private = NULL;
                bh->b_size = size;
 
                /* Link the buffer to its page */
                set_bh_page(bh, page, offset);
 
-               bh->b_end_io = NULL;
+               init_buffer(bh, NULL, NULL);
        }
        return head;
 /*
@@ -1170,7 +1166,7 @@ failed:
  * some of those buffers may be aliases of filesystem data.
  * grow_dev_page() will go BUG() if this happens.
  */
-static inline int
+static int
 grow_buffers(struct block_device *bdev, sector_t block, int size)
 {
        struct page *page;
@@ -1396,7 +1392,7 @@ static void bh_lru_install(struct buffer_head *bh)
 /*
  * Look up the bh in this cpu's LRU.  If it's there, move it to the head.
  */
-static inline struct buffer_head *
+static struct buffer_head *
 lookup_bh_lru(struct block_device *bdev, sector_t block, int size)
 {
        struct buffer_head *ret = NULL;
@@ -1438,7 +1434,7 @@ __find_get_block(struct block_device *bdev, sector_t block, int size)
        struct buffer_head *bh = lookup_bh_lru(bdev, block, size);
 
        if (bh == NULL) {
-               bh = __find_get_block_slow(bdev, block, size);
+               bh = __find_get_block_slow(bdev, block);
                if (bh)
                        bh_lru_install(bh);
        }
@@ -1478,8 +1474,10 @@ EXPORT_SYMBOL(__getblk);
 void __breadahead(struct block_device *bdev, sector_t block, int size)
 {
        struct buffer_head *bh = __getblk(bdev, block, size);
-       ll_rw_block(READA, 1, &bh);
-       brelse(bh);
+       if (likely(bh)) {
+               ll_rw_block(READA, 1, &bh);
+               brelse(bh);
+       }
 }
 EXPORT_SYMBOL(__breadahead);
 
@@ -1497,7 +1495,7 @@ __bread(struct block_device *bdev, sector_t block, int size)
 {
        struct buffer_head *bh = __getblk(bdev, block, size);
 
-       if (!buffer_uptodate(bh))
+       if (likely(bh) && !buffer_uptodate(bh))
                bh = __bread_slow(bh);
        return bh;
 }
@@ -1544,7 +1542,7 @@ EXPORT_SYMBOL(set_bh_page);
 /*
  * Called when truncating a buffer on a page completely.
  */
-static inline void discard_buffer(struct buffer_head * bh)
+static void discard_buffer(struct buffer_head * bh)
 {
        lock_buffer(bh);
        clear_buffer_dirty(bh);
@@ -1637,6 +1635,15 @@ out:
 }
 EXPORT_SYMBOL(block_invalidatepage);
 
+int do_invalidatepage(struct page *page, unsigned long offset)
+{
+       int (*invalidatepage)(struct page *, unsigned long);
+       invalidatepage = page->mapping->a_ops->invalidatepage;
+       if (invalidatepage == NULL)
+               invalidatepage = block_invalidatepage;
+       return (*invalidatepage)(page, offset);
+}
+
 /*
  * We attach and possibly dirty the buffers atomically wrt
  * __set_page_dirty_buffers() via private_lock.  try_to_free_buffers
@@ -1694,7 +1701,7 @@ void unmap_underlying_metadata(struct block_device *bdev, sector_t block)
 
        might_sleep();
 
-       old_bh = __find_get_block_slow(bdev, block, 0);
+       old_bh = __find_get_block_slow(bdev, block);
        if (old_bh) {
                clear_buffer_dirty(old_bh);
                wait_on_buffer(old_bh);
@@ -1757,7 +1764,7 @@ static int __block_write_full_page(struct inode *inode, struct page *page,
         * handle that here by just cleaning them.
         */
 
-       block = page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
+       block = (sector_t)page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
        head = page_buffers(page);
        bh = head;
 
@@ -2149,11 +2156,12 @@ int block_read_full_page(struct page *page, get_block_t *get_block)
  * truncates.  Uses prepare/commit_write to allow the filesystem to
  * deal with the hole.  
  */
-int generic_cont_expand(struct inode *inode, loff_t size)
+static int __generic_cont_expand(struct inode *inode, loff_t size,
+                                pgoff_t index, unsigned int offset)
 {
        struct address_space *mapping = inode->i_mapping;
        struct page *page;
-       unsigned long index, offset, limit;
+       unsigned long limit;
        int err;
 
        err = -EFBIG;
@@ -2165,24 +2173,24 @@ int generic_cont_expand(struct inode *inode, loff_t size)
        if (size > inode->i_sb->s_maxbytes)
                goto out;
 
-       offset = (size & (PAGE_CACHE_SIZE-1)); /* Within page */
-
-       /* ugh.  in prepare/commit_write, if from==to==start of block, we 
-       ** skip the prepare.  make sure we never send an offset for the start
-       ** of a block
-       */
-       if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
-               offset++;
-       }
-       index = size >> PAGE_CACHE_SHIFT;
        err = -ENOMEM;
        page = grab_cache_page(mapping, index);
        if (!page)
                goto out;
        err = mapping->a_ops->prepare_write(NULL, page, offset, offset);
-       if (!err) {
-               err = mapping->a_ops->commit_write(NULL, page, offset, offset);
+       if (err) {
+               /*
+                * ->prepare_write() may have instantiated a few blocks
+                * outside i_size.  Trim these off again.
+                */
+               unlock_page(page);
+               page_cache_release(page);
+               vmtruncate(inode, inode->i_size);
+               goto out;
        }
+
+       err = mapping->a_ops->commit_write(NULL, page, offset, offset);
+
        unlock_page(page);
        page_cache_release(page);
        if (err > 0)
@@ -2191,6 +2199,36 @@ out:
        return err;
 }
 
+int generic_cont_expand(struct inode *inode, loff_t size)
+{
+       pgoff_t index;
+       unsigned int offset;
+
+       offset = (size & (PAGE_CACHE_SIZE - 1)); /* Within page */
+
+       /* ugh.  in prepare/commit_write, if from==to==start of block, we
+       ** skip the prepare.  make sure we never send an offset for the start
+       ** of a block
+       */
+       if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
+               /* caller must handle this extra byte. */
+               offset++;
+       }
+       index = size >> PAGE_CACHE_SHIFT;
+
+       return __generic_cont_expand(inode, size, index, offset);
+}
+
+int generic_cont_expand_simple(struct inode *inode, loff_t size)
+{
+       loff_t pos = size - 1;
+       pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+       unsigned int offset = (pos & (PAGE_CACHE_SIZE - 1)) + 1;
+
+       /* prepare/commit_write can handle even if from==to==start of block. */
+       return __generic_cont_expand(inode, size, index, offset);
+}
+
 /*
  * For moronic filesystems that do not allow holes in file.
  * We may have to extend the file.
@@ -2302,7 +2340,7 @@ int generic_commit_write(struct file *file, struct page *page,
        __block_commit_write(inode,page,from,to);
        /*
         * No need to use i_size_read() here, the i_size
-        * cannot change under us because we hold i_sem.
+        * cannot change under us because we hold i_mutex.
         */
        if (pos > inode->i_size) {
                i_size_write(inode, pos);
@@ -2599,7 +2637,7 @@ int block_truncate_page(struct address_space *mapping,
        pgoff_t index = from >> PAGE_CACHE_SHIFT;
        unsigned offset = from & (PAGE_CACHE_SIZE-1);
        unsigned blocksize;
-       pgoff_t iblock;
+       sector_t iblock;
        unsigned length, pos;
        struct inode *inode = mapping->host;
        struct page *page;
@@ -2615,7 +2653,7 @@ int block_truncate_page(struct address_space *mapping,
                return 0;
 
        length = blocksize - length;
-       iblock = index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
+       iblock = (sector_t)index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
        
        page = grab_cache_page(mapping, index);
        err = -ENOMEM;
@@ -2696,7 +2734,7 @@ int block_write_full_page(struct page *page, get_block_t *get_block,
                 * they may have been added in ext3_writepage().  Make them
                 * freeable here, so the page does not leak.
                 */
-               block_invalidatepage(page, 0);
+               do_invalidatepage(page, 0);
                unlock_page(page);
                return 0; /* don't care */
        }
@@ -3012,6 +3050,66 @@ asmlinkage long sys_bdflush(int func, long data)
        return 0;
 }
 
+/*
+ * Migration function for pages with buffers. This function can only be used
+ * if the underlying filesystem guarantees that no other references to "page"
+ * exist.
+ */
+#ifdef CONFIG_MIGRATION
+int buffer_migrate_page(struct page *newpage, struct page *page)
+{
+       struct address_space *mapping = page->mapping;
+       struct buffer_head *bh, *head;
+
+       if (!mapping)
+               return -EAGAIN;
+
+       if (!page_has_buffers(page))
+               return migrate_page(newpage, page);
+
+       head = page_buffers(page);
+
+       if (migrate_page_remove_references(newpage, page, 3))
+               return -EAGAIN;
+
+       bh = head;
+       do {
+               get_bh(bh);
+               lock_buffer(bh);
+               bh = bh->b_this_page;
+
+       } while (bh != head);
+
+       ClearPagePrivate(page);
+       set_page_private(newpage, page_private(page));
+       set_page_private(page, 0);
+       put_page(page);
+       get_page(newpage);
+
+       bh = head;
+       do {
+               set_bh_page(bh, newpage, bh_offset(bh));
+               bh = bh->b_this_page;
+
+       } while (bh != head);
+
+       SetPagePrivate(newpage);
+
+       migrate_page_copy(newpage, page);
+
+       bh = head;
+       do {
+               unlock_buffer(bh);
+               put_bh(bh);
+               bh = bh->b_this_page;
+
+       } while (bh != head);
+
+       return 0;
+}
+EXPORT_SYMBOL(buffer_migrate_page);
+#endif
+
 /*
  * Buffer-head allocation
  */
@@ -3134,6 +3232,7 @@ EXPORT_SYMBOL(fsync_bdev);
 EXPORT_SYMBOL(generic_block_bmap);
 EXPORT_SYMBOL(generic_commit_write);
 EXPORT_SYMBOL(generic_cont_expand);
+EXPORT_SYMBOL(generic_cont_expand_simple);
 EXPORT_SYMBOL(init_buffer);
 EXPORT_SYMBOL(invalidate_bdev);
 EXPORT_SYMBOL(ll_rw_block);