Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
[safe/jmp/linux-2.6] / fs / jfs / jfs_metapage.c
index 6c5485d..07b6c5d 100644 (file)
@@ -4,25 +4,27 @@
  *
  *   This program is free software;  you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or 
+ *   the Free Software Foundation; either version 2 of the License, or
  *   (at your option) any later version.
- * 
+ *
  *   This program is distributed in the hope that it will be useful,
  *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
  *   the GNU General Public License for more details.
  *
  *   You should have received a copy of the GNU General Public License
- *   along with this program;  if not, write to the Free Software 
+ *   along with this program;  if not, write to the Free Software
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
 #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"
@@ -39,11 +41,11 @@ static struct {
 #endif
 
 #define metapage_locked(mp) test_bit(META_locked, &(mp)->flag)
-#define trylock_metapage(mp) test_and_set_bit(META_locked, &(mp)->flag)
+#define trylock_metapage(mp) test_and_set_bit_lock(META_locked, &(mp)->flag)
 
 static inline void unlock_metapage(struct metapage *mp)
 {
-       clear_bit(META_locked, &mp->flag);
+       clear_bit_unlock(META_locked, &mp->flag);
        wake_up(&mp->wait);
 }
 
@@ -56,7 +58,7 @@ static inline void __lock_metapage(struct metapage *mp)
                set_current_state(TASK_UNINTERRUPTIBLE);
                if (metapage_locked(mp)) {
                        unlock_page(mp->page);
-                       schedule();
+                       io_schedule();
                        lock_page(mp->page);
                }
        } while (trylock_metapage(mp));
@@ -74,7 +76,7 @@ static inline void lock_metapage(struct metapage *mp)
 }
 
 #define METAPOOL_MIN_PAGES 32
-static kmem_cache_t *metapage_cache;
+static struct kmem_cache *metapage_cache;
 static mempool_t *metapage_mempool;
 
 #define MPS_PER_PAGE (PAGE_CACHE_SIZE >> L2PSIZE)
@@ -86,9 +88,9 @@ struct meta_anchor {
        atomic_t io_count;
        struct metapage *mp[MPS_PER_PAGE];
 };
-#define mp_anchor(page) ((struct meta_anchor *)page->private)
+#define mp_anchor(page) ((struct meta_anchor *)page_private(page))
 
-static inline struct metapage *page_to_mp(struct page *page, uint offset)
+static inline struct metapage *page_to_mp(struct page *page, int offset)
 {
        if (!PagePrivate(page))
                return NULL;
@@ -104,11 +106,10 @@ static inline int insert_metapage(struct page *page, struct metapage *mp)
        if (PagePrivate(page))
                a = mp_anchor(page);
        else {
-               a = kmalloc(sizeof(struct meta_anchor), GFP_NOFS);
+               a = kzalloc(sizeof(struct meta_anchor), GFP_NOFS);
                if (!a)
                        return -ENOMEM;
-               memset(a, 0, sizeof(struct meta_anchor));
-               page->private = (unsigned long)a;
+               set_page_private(page, (unsigned long)a);
                SetPagePrivate(page);
                kmap(page);
        }
@@ -136,7 +137,7 @@ static inline void remove_metapage(struct page *page, struct metapage *mp)
        a->mp[index] = NULL;
        if (--a->mp_count == 0) {
                kfree(a);
-               page->private = 0;
+               set_page_private(page, 0);
                ClearPagePrivate(page);
                kunmap(page);
        }
@@ -154,15 +155,15 @@ static inline void dec_io(struct page *page, void (*handler) (struct page *))
 }
 
 #else
-static inline struct metapage *page_to_mp(struct page *page, uint offset)
+static inline struct metapage *page_to_mp(struct page *page, int offset)
 {
-       return PagePrivate(page) ? (struct metapage *)page->private : NULL;
+       return PagePrivate(page) ? (struct metapage *)page_private(page) : NULL;
 }
 
 static inline int insert_metapage(struct page *page, struct metapage *mp)
 {
        if (mp) {
-               page->private = (unsigned long)mp;
+               set_page_private(page, (unsigned long)mp);
                SetPagePrivate(page);
                kmap(page);
        }
@@ -171,7 +172,7 @@ static inline int insert_metapage(struct page *page, struct metapage *mp)
 
 static inline void remove_metapage(struct page *page, struct metapage *mp)
 {
-       page->private = 0;
+       set_page_private(page, 0);
        ClearPagePrivate(page);
        kunmap(page);
 }
@@ -181,24 +182,21 @@ static inline void remove_metapage(struct page *page, struct metapage *mp)
 
 #endif
 
-static void init_once(void *foo, kmem_cache_t *cachep, unsigned long flags)
+static void init_once(void *foo)
 {
        struct metapage *mp = (struct metapage *)foo;
 
-       if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
-           SLAB_CTOR_CONSTRUCTOR) {
-               mp->lid = 0;
-               mp->lsn = 0;
-               mp->flag = 0;
-               mp->data = NULL;
-               mp->clsn = 0;
-               mp->log = NULL;
-               set_bit(META_free, &mp->flag);
-               init_waitqueue_head(&mp->wait);
-       }
+       mp->lid = 0;
+       mp->lsn = 0;
+       mp->flag = 0;
+       mp->data = NULL;
+       mp->clsn = 0;
+       mp->log = NULL;
+       set_bit(META_free, &mp->flag);
+       init_waitqueue_head(&mp->wait);
 }
 
-static inline struct metapage *alloc_metapage(unsigned int gfp_mask)
+static inline struct metapage *alloc_metapage(gfp_t gfp_mask)
 {
        return mempool_alloc(metapage_mempool, gfp_mask);
 }
@@ -217,12 +215,12 @@ int __init metapage_init(void)
         * Allocate the metapage structures
         */
        metapage_cache = kmem_cache_create("jfs_mp", sizeof(struct metapage),
-                                          0, 0, init_once, NULL);
+                                          0, 0, init_once);
        if (metapage_cache == NULL)
                return -ENOMEM;
 
-       metapage_mempool = mempool_create(METAPOOL_MIN_PAGES, mempool_alloc_slab,
-                                         mempool_free_slab, metapage_cache);
+       metapage_mempool = mempool_create_slab_pool(METAPOOL_MIN_PAGES,
+                                                   metapage_cache);
 
        if (metapage_mempool == NULL) {
                kmem_cache_destroy(metapage_cache);
@@ -253,12 +251,12 @@ static inline void drop_metapage(struct page *page, struct metapage *mp)
  */
 
 static sector_t metapage_get_blocks(struct inode *inode, sector_t lblock,
-                                   unsigned int *len)
+                                   int *len)
 {
        int rc = 0;
        int xflag;
        s64 xaddr;
-       sector_t file_blocks = (inode->i_size + inode->i_blksize - 1) >>
+       sector_t file_blocks = (inode->i_size + inode->i_sb->s_blocksize - 1) >>
                               inode->i_blkbits;
 
        if (lblock >= file_blocks)
@@ -284,14 +282,10 @@ static void last_read_complete(struct page *page)
        unlock_page(page);
 }
 
-static int metapage_read_end_io(struct bio *bio, unsigned int bytes_done,
-                               int err)
+static void metapage_read_end_io(struct bio *bio, int err)
 {
        struct page *page = bio->bi_private;
 
-       if (bio->bi_size)
-               return 1;
-
        if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) {
                printk(KERN_ERR "metapage_read_end_io: I/O error\n");
                SetPageError(page);
@@ -299,8 +293,6 @@ static int metapage_read_end_io(struct bio *bio, unsigned int bytes_done,
 
        dec_io(page, last_read_complete);
        bio_put(bio);
-
-       return 0;
 }
 
 static void remove_from_logsync(struct metapage *mp)
@@ -345,47 +337,45 @@ static void last_write_complete(struct page *page)
        end_page_writeback(page);
 }
 
-static int metapage_write_end_io(struct bio *bio, unsigned int bytes_done,
-                                int err)
+static void metapage_write_end_io(struct bio *bio, int err)
 {
        struct page *page = bio->bi_private;
 
        BUG_ON(!PagePrivate(page));
 
-       if (bio->bi_size)
-               return 1;
-
        if (! test_bit(BIO_UPTODATE, &bio->bi_flags)) {
                printk(KERN_ERR "metapage_write_end_io: I/O error\n");
                SetPageError(page);
        }
        dec_io(page, last_write_complete);
        bio_put(bio);
-       return 0;
 }
 
 static int metapage_writepage(struct page *page, struct writeback_control *wbc)
 {
        struct bio *bio = NULL;
-       unsigned int block_offset;      /* block offset of mp within page */
+       int block_offset;       /* block offset of mp within page */
        struct inode *inode = page->mapping->host;
-       unsigned int blocks_per_mp = JFS_SBI(inode->i_sb)->nbperpage;
-       unsigned int len;
-       unsigned int xlen;
+       int blocks_per_mp = JFS_SBI(inode->i_sb)->nbperpage;
+       int len;
+       int xlen;
        struct metapage *mp;
        int redirty = 0;
        sector_t lblock;
+       int nr_underway = 0;
        sector_t pblock;
        sector_t next_block = 0;
        sector_t page_start;
        unsigned long bio_bytes = 0;
        unsigned long bio_offset = 0;
-       unsigned int offset;
+       int offset;
+       int bad_blocks = 0;
 
        page_start = (sector_t)page->index <<
                     (PAGE_CACHE_SHIFT - inode->i_blkbits);
        BUG_ON(!PageLocked(page));
        BUG_ON(PageWriteback(page));
+       set_page_writeback(page);
 
        for (offset = 0; offset < PAGE_CACHE_SIZE; offset += PSIZE) {
                mp = page_to_mp(page, offset);
@@ -395,10 +385,17 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc)
 
                if (mp->nohomeok && !test_bit(META_forcewrite, &mp->flag)) {
                        redirty = 1;
+                       /*
+                        * Make sure this page isn't blocked indefinitely.
+                        * If the journal isn't undergoing I/O, push it
+                        */
+                       if (mp->log && !(mp->log->cflag & logGC_PAGEOUT))
+                               jfs_flush_journal(mp->log, 0);
                        continue;
                }
 
                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) {
@@ -407,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,21 +418,22 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc)
                        if (!bio->bi_size)
                                goto dump_bio;
                        submit_bio(WRITE, bio);
+                       nr_underway++;
                        bio = NULL;
-               } else {
-                       set_page_writeback(page);
+               } else
                        inc_io(page);
-               }
                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, (uint) JFS_SBI(inode->i_sb)->nbperpage);
+               len = min(xlen, (int)JFS_SBI(inode->i_sb)->nbperpage);
 
                bio = bio_alloc(GFP_NOFS, 1);
                bio->bi_bdev = inode->i_sb->s_bdev;
@@ -456,26 +453,36 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc)
                                goto add_failed;
                if (!bio->bi_size)
                        goto dump_bio;
-               
+
                submit_bio(WRITE, bio);
+               nr_underway++;
        }
        if (redirty)
                redirty_page_for_writepage(wbc, page);
 
        unlock_page(page);
 
+       if (bad_blocks)
+               goto err_out;
+
+       if (nr_underway == 0)
+               end_page_writeback(page);
+
        return 0;
 add_failed:
        /* We should never reach here, since we're only adding one vec */
        printk(KERN_ERR "JFS: bio_add_page failed unexpectedly\n");
        goto skip;
 dump_bio:
-       dump_mem("bio", bio, sizeof(*bio));
+       print_hex_dump(KERN_ERR, "JFS: dump of bio: ", DUMP_PREFIX_ADDRESS, 16,
+                      4, bio, sizeof(*bio), 0);
 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;
 }
 
@@ -483,13 +490,13 @@ static int metapage_readpage(struct file *fp, struct page *page)
 {
        struct inode *inode = page->mapping->host;
        struct bio *bio = NULL;
-       unsigned int block_offset;
-       unsigned int blocks_per_page = PAGE_CACHE_SIZE >> inode->i_blkbits;
+       int block_offset;
+       int blocks_per_page = PAGE_CACHE_SIZE >> inode->i_blkbits;
        sector_t page_start;    /* address of page in fs blocks */
        sector_t pblock;
-       unsigned int xlen;
+       int xlen;
        unsigned int len;
-       unsigned int offset;
+       int offset;
 
        BUG_ON(!PageLocked(page));
        page_start = (sector_t)page->index <<
@@ -534,11 +541,11 @@ add_failed:
        return -EIO;
 }
 
-static int metapage_releasepage(struct page *page, int gfp_mask)
+static int metapage_releasepage(struct page *page, gfp_t gfp_mask)
 {
        struct metapage *mp;
-       int busy = 0;
-       unsigned int offset;
+       int ret = 1;
+       int offset;
 
        for (offset = 0; offset < PAGE_CACHE_SIZE; offset += PSIZE) {
                mp = page_to_mp(page, offset);
@@ -547,44 +554,32 @@ static int metapage_releasepage(struct page *page, int gfp_mask)
                        continue;
 
                jfs_info("metapage_releasepage: mp = 0x%p", mp);
-               if (mp->count || mp->nohomeok) {
+               if (mp->count || mp->nohomeok ||
+                   test_bit(META_dirty, &mp->flag)) {
                        jfs_info("count = %ld, nohomeok = %d", mp->count,
                                 mp->nohomeok);
-                       busy = 1;
+                       ret = 0;
                        continue;
                }
-               wait_on_page_writeback(page);
-               //WARN_ON(test_bit(META_dirty, &mp->flag));
-               if (test_bit(META_dirty, &mp->flag)) {
-                       dump_mem("dirty mp in metapage_releasepage", mp,
-                                sizeof(struct metapage));
-                       dump_mem("page", page, sizeof(struct page));
-                       dump_stack();
-               }
-               WARN_ON(mp->lsn);
                if (mp->lsn)
                        remove_from_logsync(mp);
                remove_metapage(page, mp);
                INCREMENT(mpStat.pagefree);
                free_metapage(mp);
        }
-       if (busy)
-               return -1;
-
-       return 0;
+       return ret;
 }
 
-static int metapage_invalidatepage(struct page *page, unsigned long offset)
+static void metapage_invalidatepage(struct page *page, unsigned long offset)
 {
        BUG_ON(offset);
 
-       if (PageWriteback(page))
-               return 0;
+       BUG_ON(PageWriteback(page));
 
-       return metapage_releasepage(page, 0);
+       metapage_releasepage(page, 0);
 }
 
-struct address_space_operations jfs_metapage_aops = {
+const struct address_space_operations jfs_metapage_aops = {
        .readpage       = metapage_readpage,
        .writepage      = metapage_writepage,
        .sync_page      = block_sync_page,
@@ -639,10 +634,9 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock,
                }
                SetPageUptodate(page);
        } else {
-               page = read_cache_page(mapping, page_index,
-                           (filler_t *)mapping->a_ops->readpage, NULL);
-               if (IS_ERR(page)) {
-                       jfs_err("read_cache_page failed!");
+               page = read_mapping_page(mapping, page_index, NULL);
+               if (IS_ERR(page) || !PageUptodate(page)) {
+                       jfs_err("read_mapping_page failed!");
                        return NULL;
                }
                lock_page(page);
@@ -656,7 +650,7 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock,
                        jfs_err("logical_size = %d, size = %d",
                                mp->logical_size, size);
                        dump_stack();
-                       goto unlock; 
+                       goto unlock;
                }
                mp->count++;
                lock_metapage(mp);
@@ -666,7 +660,7 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock,
                                          "__get_metapage: using a "
                                          "discarded metapage");
                                discard_metapage(mp);
-                               goto unlock; 
+                               goto unlock;
                        }
                        clear_bit(META_discard, &mp->flag);
                }
@@ -772,30 +766,9 @@ void release_metapage(struct metapage * mp)
        } else if (mp->lsn)     /* discard_metapage doesn't remove it */
                remove_from_logsync(mp);
 
-#if MPS_PER_PAGE == 1
-       /*
-        * If we know this is the only thing in the page, we can throw
-        * the page out of the page cache.  If pages are larger, we
-        * don't want to do this.
-        */
-
-       /* Retest mp->count since we may have released page lock */
-       if (test_bit(META_discard, &mp->flag) && !mp->count) {
-               clear_page_dirty(page);
-               ClearPageUptodate(page);
-#ifdef _NOT_YET
-               if (page->mapping) {
-               /* Remove from page cache and page cache reference */
-                       remove_from_page_cache(page);
-                       page_cache_release(page);
-                       metapage_releasepage(page, 0);
-               }
-#endif
-       }
-#else
        /* Try to keep metapages from using up too much memory */
        drop_metapage(page, mp);
-#endif
+
        unlock_page(page);
        page_cache_release(page);
 }
@@ -841,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"
@@ -856,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