123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 |
- /*
- * Copyright (C) International Business Machines Corp., 2000-2003
- * Portions Copyright (C) Christoph Hellwig, 2001-2002
- *
- * 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
- * (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
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #include <linux/fs.h>
- #include <linux/init.h>
- #include <linux/buffer_head.h>
- #include <linux/mempool.h>
- #include <linux/delay.h>
- #include "jfs_incore.h"
- #include "jfs_superblock.h"
- #include "jfs_filsys.h"
- #include "jfs_metapage.h"
- #include "jfs_txnmgr.h"
- #include "jfs_debug.h"
- static DEFINE_SPINLOCK(meta_lock);
- #ifdef CONFIG_JFS_STATISTICS
- static struct {
- uint pagealloc; /* # of page allocations */
- uint pagefree; /* # of page frees */
- uint lockwait; /* # of sleeping lock_metapage() calls */
- } mpStat;
- #endif
- #define HASH_BITS 10 /* This makes hash_table 1 4K page */
- #define HASH_SIZE (1 << HASH_BITS)
- static struct metapage **hash_table = NULL;
- static unsigned long hash_order;
- static inline int metapage_locked(struct metapage *mp)
- {
- return test_bit(META_locked, &mp->flag);
- }
- static inline int trylock_metapage(struct metapage *mp)
- {
- return test_and_set_bit(META_locked, &mp->flag);
- }
- static inline void unlock_metapage(struct metapage *mp)
- {
- clear_bit(META_locked, &mp->flag);
- wake_up(&mp->wait);
- }
- static void __lock_metapage(struct metapage *mp)
- {
- DECLARE_WAITQUEUE(wait, current);
- INCREMENT(mpStat.lockwait);
- add_wait_queue_exclusive(&mp->wait, &wait);
- do {
- set_current_state(TASK_UNINTERRUPTIBLE);
- if (metapage_locked(mp)) {
- spin_unlock(&meta_lock);
- schedule();
- spin_lock(&meta_lock);
- }
- } while (trylock_metapage(mp));
- __set_current_state(TASK_RUNNING);
- remove_wait_queue(&mp->wait, &wait);
- }
- /* needs meta_lock */
- static inline void lock_metapage(struct metapage *mp)
- {
- if (trylock_metapage(mp))
- __lock_metapage(mp);
- }
- #define METAPOOL_MIN_PAGES 32
- static kmem_cache_t *metapage_cache;
- static mempool_t *metapage_mempool;
- static void init_once(void *foo, kmem_cache_t *cachep, unsigned long flags)
- {
- 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);
- }
- }
- static inline struct metapage *alloc_metapage(int gfp_mask)
- {
- return mempool_alloc(metapage_mempool, gfp_mask);
- }
- static inline void free_metapage(struct metapage *mp)
- {
- mp->flag = 0;
- set_bit(META_free, &mp->flag);
- mempool_free(mp, metapage_mempool);
- }
- int __init metapage_init(void)
- {
- /*
- * Allocate the metapage structures
- */
- metapage_cache = kmem_cache_create("jfs_mp", sizeof(struct metapage),
- 0, 0, init_once, NULL);
- if (metapage_cache == NULL)
- return -ENOMEM;
- metapage_mempool = mempool_create(METAPOOL_MIN_PAGES, mempool_alloc_slab,
- mempool_free_slab, metapage_cache);
- if (metapage_mempool == NULL) {
- kmem_cache_destroy(metapage_cache);
- return -ENOMEM;
- }
- /*
- * Now the hash list
- */
- for (hash_order = 0;
- ((PAGE_SIZE << hash_order) / sizeof(void *)) < HASH_SIZE;
- hash_order++);
- hash_table =
- (struct metapage **) __get_free_pages(GFP_KERNEL, hash_order);
- assert(hash_table);
- memset(hash_table, 0, PAGE_SIZE << hash_order);
- return 0;
- }
- void metapage_exit(void)
- {
- mempool_destroy(metapage_mempool);
- kmem_cache_destroy(metapage_cache);
- }
- /*
- * Basically same hash as in pagemap.h, but using our hash table
- */
- static struct metapage **meta_hash(struct address_space *mapping,
- unsigned long index)
- {
- #define i (((unsigned long)mapping)/ \
- (sizeof(struct inode) & ~(sizeof(struct inode) -1 )))
- #define s(x) ((x) + ((x) >> HASH_BITS))
- return hash_table + (s(i + index) & (HASH_SIZE - 1));
- #undef i
- #undef s
- }
- static struct metapage *search_hash(struct metapage ** hash_ptr,
- struct address_space *mapping,
- unsigned long index)
- {
- struct metapage *ptr;
- for (ptr = *hash_ptr; ptr; ptr = ptr->hash_next) {
- if ((ptr->mapping == mapping) && (ptr->index == index))
- return ptr;
- }
- return NULL;
- }
- static void add_to_hash(struct metapage * mp, struct metapage ** hash_ptr)
- {
- if (*hash_ptr)
- (*hash_ptr)->hash_prev = mp;
- mp->hash_prev = NULL;
- mp->hash_next = *hash_ptr;
- *hash_ptr = mp;
- }
- static void remove_from_hash(struct metapage * mp, struct metapage ** hash_ptr)
- {
- if (mp->hash_prev)
- mp->hash_prev->hash_next = mp->hash_next;
- else {
- assert(*hash_ptr == mp);
- *hash_ptr = mp->hash_next;
- }
- if (mp->hash_next)
- mp->hash_next->hash_prev = mp->hash_prev;
- }
- struct metapage *__get_metapage(struct inode *inode, unsigned long lblock,
- unsigned int size, int absolute,
- unsigned long new)
- {
- struct metapage **hash_ptr;
- int l2BlocksPerPage;
- int l2bsize;
- struct address_space *mapping;
- struct metapage *mp;
- unsigned long page_index;
- unsigned long page_offset;
- jfs_info("__get_metapage: inode = 0x%p, lblock = 0x%lx", inode, lblock);
- if (absolute)
- mapping = inode->i_sb->s_bdev->bd_inode->i_mapping;
- else {
- /*
- * If an nfs client tries to read an inode that is larger
- * than any existing inodes, we may try to read past the
- * end of the inode map
- */
- if ((lblock << inode->i_blkbits) >= inode->i_size)
- return NULL;
- mapping = inode->i_mapping;
- }
- hash_ptr = meta_hash(mapping, lblock);
- again:
- spin_lock(&meta_lock);
- mp = search_hash(hash_ptr, mapping, lblock);
- if (mp) {
- page_found:
- if (test_bit(META_stale, &mp->flag)) {
- spin_unlock(&meta_lock);
- msleep(1);
- goto again;
- }
- mp->count++;
- lock_metapage(mp);
- spin_unlock(&meta_lock);
- if (test_bit(META_discard, &mp->flag)) {
- if (!new) {
- jfs_error(inode->i_sb,
- "__get_metapage: using a "
- "discarded metapage");
- release_metapage(mp);
- return NULL;
- }
- clear_bit(META_discard, &mp->flag);
- }
- jfs_info("__get_metapage: found 0x%p, in hash", mp);
- if (mp->logical_size != size) {
- jfs_error(inode->i_sb,
- "__get_metapage: mp->logical_size != size");
- release_metapage(mp);
- return NULL;
- }
- } else {
- l2bsize = inode->i_blkbits;
- l2BlocksPerPage = PAGE_CACHE_SHIFT - l2bsize;
- page_index = lblock >> l2BlocksPerPage;
- page_offset = (lblock - (page_index << l2BlocksPerPage)) <<
- l2bsize;
- if ((page_offset + size) > PAGE_CACHE_SIZE) {
- spin_unlock(&meta_lock);
- jfs_err("MetaData crosses page boundary!!");
- return NULL;
- }
-
- /*
- * Locks held on aggregate inode pages are usually
- * not held long, and they are taken in critical code
- * paths (committing dirty inodes, txCommit thread)
- *
- * Attempt to get metapage without blocking, tapping into
- * reserves if necessary.
- */
- mp = NULL;
- if (JFS_IP(inode)->fileset == AGGREGATE_I) {
- mp = alloc_metapage(GFP_ATOMIC);
- if (!mp) {
- /*
- * mempool is supposed to protect us from
- * failing here. We will try a blocking
- * call, but a deadlock is possible here
- */
- printk(KERN_WARNING
- "__get_metapage: atomic call to mempool_alloc failed.\n");
- printk(KERN_WARNING
- "Will attempt blocking call\n");
- }
- }
- if (!mp) {
- struct metapage *mp2;
- spin_unlock(&meta_lock);
- mp = alloc_metapage(GFP_NOFS);
- spin_lock(&meta_lock);
- /* we dropped the meta_lock, we need to search the
- * hash again.
- */
- mp2 = search_hash(hash_ptr, mapping, lblock);
- if (mp2) {
- free_metapage(mp);
- mp = mp2;
- goto page_found;
- }
- }
- mp->flag = 0;
- lock_metapage(mp);
- if (absolute)
- set_bit(META_absolute, &mp->flag);
- mp->xflag = COMMIT_PAGE;
- mp->count = 1;
- atomic_set(&mp->nohomeok,0);
- mp->mapping = mapping;
- mp->index = lblock;
- mp->page = NULL;
- mp->logical_size = size;
- add_to_hash(mp, hash_ptr);
- spin_unlock(&meta_lock);
- if (new) {
- jfs_info("__get_metapage: Calling grab_cache_page");
- mp->page = grab_cache_page(mapping, page_index);
- if (!mp->page) {
- jfs_err("grab_cache_page failed!");
- goto freeit;
- } else {
- INCREMENT(mpStat.pagealloc);
- unlock_page(mp->page);
- }
- } else {
- jfs_info("__get_metapage: Calling read_cache_page");
- mp->page = read_cache_page(mapping, lblock,
- (filler_t *)mapping->a_ops->readpage, NULL);
- if (IS_ERR(mp->page)) {
- jfs_err("read_cache_page failed!");
- goto freeit;
- } else
- INCREMENT(mpStat.pagealloc);
- }
- mp->data = kmap(mp->page) + page_offset;
- }
- if (new)
- memset(mp->data, 0, PSIZE);
- jfs_info("__get_metapage: returning = 0x%p", mp);
- return mp;
- freeit:
- spin_lock(&meta_lock);
- remove_from_hash(mp, hash_ptr);
- free_metapage(mp);
- spin_unlock(&meta_lock);
- return NULL;
- }
- void hold_metapage(struct metapage * mp, int force)
- {
- spin_lock(&meta_lock);
- mp->count++;
- if (force) {
- ASSERT (!(test_bit(META_forced, &mp->flag)));
- if (trylock_metapage(mp))
- set_bit(META_forced, &mp->flag);
- } else
- lock_metapage(mp);
- spin_unlock(&meta_lock);
- }
- static void __write_metapage(struct metapage * mp)
- {
- int l2bsize = mp->mapping->host->i_blkbits;
- int l2BlocksPerPage = PAGE_CACHE_SHIFT - l2bsize;
- unsigned long page_index;
- unsigned long page_offset;
- int rc;
- jfs_info("__write_metapage: mp = 0x%p", mp);
- page_index = mp->page->index;
- page_offset =
- (mp->index - (page_index << l2BlocksPerPage)) << l2bsize;
- lock_page(mp->page);
- rc = mp->mapping->a_ops->prepare_write(NULL, mp->page, page_offset,
- page_offset +
- mp->logical_size);
- if (rc) {
- jfs_err("prepare_write return %d!", rc);
- ClearPageUptodate(mp->page);
- unlock_page(mp->page);
- clear_bit(META_dirty, &mp->flag);
- return;
- }
- rc = mp->mapping->a_ops->commit_write(NULL, mp->page, page_offset,
- page_offset +
- mp->logical_size);
- if (rc) {
- jfs_err("commit_write returned %d", rc);
- }
- unlock_page(mp->page);
- clear_bit(META_dirty, &mp->flag);
- jfs_info("__write_metapage done");
- }
- static inline void sync_metapage(struct metapage *mp)
- {
- struct page *page = mp->page;
- page_cache_get(page);
- lock_page(page);
- /* we're done with this page - no need to check for errors */
- if (page_has_buffers(page))
- write_one_page(page, 1);
- else
- unlock_page(page);
- page_cache_release(page);
- }
- void release_metapage(struct metapage * mp)
- {
- struct jfs_log *log;
- jfs_info("release_metapage: mp = 0x%p, flag = 0x%lx", mp, mp->flag);
- spin_lock(&meta_lock);
- if (test_bit(META_forced, &mp->flag)) {
- clear_bit(META_forced, &mp->flag);
- mp->count--;
- spin_unlock(&meta_lock);
- return;
- }
- assert(mp->count);
- if (--mp->count || atomic_read(&mp->nohomeok)) {
- unlock_metapage(mp);
- spin_unlock(&meta_lock);
- return;
- }
- if (mp->page) {
- set_bit(META_stale, &mp->flag);
- spin_unlock(&meta_lock);
- kunmap(mp->page);
- mp->data = NULL;
- if (test_bit(META_dirty, &mp->flag))
- __write_metapage(mp);
- if (test_bit(META_sync, &mp->flag)) {
- sync_metapage(mp);
- clear_bit(META_sync, &mp->flag);
- }
- if (test_bit(META_discard, &mp->flag)) {
- lock_page(mp->page);
- block_invalidatepage(mp->page, 0);
- unlock_page(mp->page);
- }
- page_cache_release(mp->page);
- mp->page = NULL;
- INCREMENT(mpStat.pagefree);
- spin_lock(&meta_lock);
- }
- if (mp->lsn) {
- /*
- * Remove metapage from logsynclist.
- */
- log = mp->log;
- LOGSYNC_LOCK(log);
- mp->log = NULL;
- mp->lsn = 0;
- mp->clsn = 0;
- log->count--;
- list_del(&mp->synclist);
- LOGSYNC_UNLOCK(log);
- }
- remove_from_hash(mp, meta_hash(mp->mapping, mp->index));
- spin_unlock(&meta_lock);
- free_metapage(mp);
- }
- void __invalidate_metapages(struct inode *ip, s64 addr, int len)
- {
- struct metapage **hash_ptr;
- unsigned long lblock;
- int l2BlocksPerPage = PAGE_CACHE_SHIFT - ip->i_blkbits;
- /* All callers are interested in block device's mapping */
- struct address_space *mapping = ip->i_sb->s_bdev->bd_inode->i_mapping;
- struct metapage *mp;
- struct page *page;
- /*
- * First, mark metapages to discard. They will eventually be
- * released, but should not be written.
- */
- for (lblock = addr; lblock < addr + len;
- lblock += 1 << l2BlocksPerPage) {
- hash_ptr = meta_hash(mapping, lblock);
- again:
- spin_lock(&meta_lock);
- mp = search_hash(hash_ptr, mapping, lblock);
- if (mp) {
- if (test_bit(META_stale, &mp->flag)) {
- spin_unlock(&meta_lock);
- msleep(1);
- goto again;
- }
- clear_bit(META_dirty, &mp->flag);
- set_bit(META_discard, &mp->flag);
- spin_unlock(&meta_lock);
- } else {
- spin_unlock(&meta_lock);
- page = find_lock_page(mapping, lblock>>l2BlocksPerPage);
- if (page) {
- block_invalidatepage(page, 0);
- unlock_page(page);
- page_cache_release(page);
- }
- }
- }
- }
- #ifdef CONFIG_JFS_STATISTICS
- int jfs_mpstat_read(char *buffer, char **start, off_t offset, int length,
- int *eof, void *data)
- {
- int len = 0;
- off_t begin;
- len += sprintf(buffer,
- "JFS Metapage statistics\n"
- "=======================\n"
- "page allocations = %d\n"
- "page frees = %d\n"
- "lock waits = %d\n",
- mpStat.pagealloc,
- mpStat.pagefree,
- mpStat.lockwait);
- begin = offset;
- *start = buffer + begin;
- len -= begin;
- if (len > length)
- len = length;
- else
- *eof = 1;
- if (len < 0)
- len = 0;
- return len;
- }
- #endif
|