Squashfs: fix breakage when page size > metadata block size
[safe/jmp/linux-2.6] / fs / jfs / jfs_metapage.c
index d1e64f2..07b6c5d 100644 (file)
 
 #include <linux/fs.h>
 #include <linux/mm.h>
+#include <linux/module.h>
 #include <linux/bio.h>
 #include <linux/init.h>
 #include <linux/buffer_head.h>
 #include <linux/mempool.h>
+#include <linux/seq_file.h>
 #include "jfs_incore.h"
 #include "jfs_superblock.h"
 #include "jfs_filsys.h"
@@ -180,7 +182,7 @@ static inline void remove_metapage(struct page *page, struct metapage *mp)
 
 #endif
 
-static void init_once(struct kmem_cache *cachep, void *foo)
+static void init_once(void *foo)
 {
        struct metapage *mp = (struct metapage *)foo;
 
@@ -367,6 +369,7 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc)
        unsigned long bio_bytes = 0;
        unsigned long bio_offset = 0;
        int offset;
+       int bad_blocks = 0;
 
        page_start = (sector_t)page->index <<
                     (PAGE_CACHE_SHIFT - inode->i_blkbits);
@@ -392,6 +395,7 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc)
                }
 
                clear_bit(META_dirty, &mp->flag);
+               set_bit(META_io, &mp->flag);
                block_offset = offset >> inode->i_blkbits;
                lblock = page_start + block_offset;
                if (bio) {
@@ -400,7 +404,6 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc)
                                len = min(xlen, blocks_per_mp);
                                xlen -= len;
                                bio_bytes += len << inode->i_blkbits;
-                               set_bit(META_io, &mp->flag);
                                continue;
                        }
                        /* Not contiguous */
@@ -422,12 +425,14 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc)
                xlen = (PAGE_CACHE_SIZE - offset) >> inode->i_blkbits;
                pblock = metapage_get_blocks(inode, lblock, &xlen);
                if (!pblock) {
-                       /* Need better error handling */
                        printk(KERN_ERR "JFS: metapage_get_blocks failed\n");
-                       dec_io(page, last_write_complete);
+                       /*
+                        * We already called inc_io(), but can't cancel it
+                        * with dec_io() until we're done with the page
+                        */
+                       bad_blocks++;
                        continue;
                }
-               set_bit(META_io, &mp->flag);
                len = min(xlen, (int)JFS_SBI(inode->i_sb)->nbperpage);
 
                bio = bio_alloc(GFP_NOFS, 1);
@@ -457,6 +462,9 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc)
 
        unlock_page(page);
 
+       if (bad_blocks)
+               goto err_out;
+
        if (nr_underway == 0)
                end_page_writeback(page);
 
@@ -472,7 +480,9 @@ skip:
        bio_put(bio);
        unlock_page(page);
        dec_io(page, last_write_complete);
-
+err_out:
+       while (bad_blocks--)
+               dec_io(page, last_write_complete);
        return -EIO;
 }
 
@@ -804,13 +814,9 @@ void __invalidate_metapages(struct inode *ip, s64 addr, int len)
 }
 
 #ifdef CONFIG_JFS_STATISTICS
-int jfs_mpstat_read(char *buffer, char **start, off_t offset, int length,
-                   int *eof, void *data)
+static int jfs_mpstat_proc_show(struct seq_file *m, void *v)
 {
-       int len = 0;
-       off_t begin;
-
-       len += sprintf(buffer,
+       seq_printf(m,
                       "JFS Metapage statistics\n"
                       "=======================\n"
                       "page allocations = %d\n"
@@ -819,19 +825,19 @@ int jfs_mpstat_read(char *buffer, char **start, off_t offset, int length,
                       mpStat.pagealloc,
                       mpStat.pagefree,
                       mpStat.lockwait);
+       return 0;
+}
 
-       begin = offset;
-       *start = buffer + begin;
-       len -= begin;
-
-       if (len > length)
-               len = length;
-       else
-               *eof = 1;
-
-       if (len < 0)
-               len = 0;
-
-       return len;
+static int jfs_mpstat_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, jfs_mpstat_proc_show, NULL);
 }
+
+const struct file_operations jfs_mpstat_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = jfs_mpstat_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
 #endif