📄 jfs_metapage.c
字号:
/* * Copyright (C) International Business Machines Corp., 2000-2005 * 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/mm.h>#include <linux/bio.h>#include <linux/init.h>#include <linux/buffer_head.h>#include <linux/mempool.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"#ifdef CONFIG_JFS_STATISTICSstatic struct { uint pagealloc; /* # of page allocations */ uint pagefree; /* # of page frees */ uint lockwait; /* # of sleeping lock_metapage() calls */} mpStat;#endif#define metapage_locked(mp) test_bit(META_locked, &(mp)->flag)#define trylock_metapage(mp) 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 inline 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)) { unlock_page(mp->page); io_schedule(); lock_page(mp->page); } } while (trylock_metapage(mp)); __set_current_state(TASK_RUNNING); remove_wait_queue(&mp->wait, &wait);}/* * Must have mp->page locked */static inline void lock_metapage(struct metapage *mp){ if (trylock_metapage(mp)) __lock_metapage(mp);}#define METAPOOL_MIN_PAGES 32static struct kmem_cache *metapage_cache;static mempool_t *metapage_mempool;#define MPS_PER_PAGE (PAGE_CACHE_SIZE >> L2PSIZE)#if MPS_PER_PAGE > 1struct meta_anchor { int mp_count; atomic_t io_count; struct metapage *mp[MPS_PER_PAGE];};#define mp_anchor(page) ((struct meta_anchor *)page_private(page))static inline struct metapage *page_to_mp(struct page *page, uint offset){ if (!PagePrivate(page)) return NULL; return mp_anchor(page)->mp[offset >> L2PSIZE];}static inline int insert_metapage(struct page *page, struct metapage *mp){ struct meta_anchor *a; int index; int l2mp_blocks; /* log2 blocks per metapage */ if (PagePrivate(page)) a = mp_anchor(page); else { a = kzalloc(sizeof(struct meta_anchor), GFP_NOFS); if (!a) return -ENOMEM; set_page_private(page, (unsigned long)a); SetPagePrivate(page); kmap(page); } if (mp) { l2mp_blocks = L2PSIZE - page->mapping->host->i_blkbits; index = (mp->index >> l2mp_blocks) & (MPS_PER_PAGE - 1); a->mp_count++; a->mp[index] = mp; } return 0;}static inline void remove_metapage(struct page *page, struct metapage *mp){ struct meta_anchor *a = mp_anchor(page); int l2mp_blocks = L2PSIZE - page->mapping->host->i_blkbits; int index; index = (mp->index >> l2mp_blocks) & (MPS_PER_PAGE - 1); BUG_ON(a->mp[index] != mp); a->mp[index] = NULL; if (--a->mp_count == 0) { kfree(a); set_page_private(page, 0); ClearPagePrivate(page); kunmap(page); }}static inline void inc_io(struct page *page){ atomic_inc(&mp_anchor(page)->io_count);}static inline void dec_io(struct page *page, void (*handler) (struct page *)){ if (atomic_dec_and_test(&mp_anchor(page)->io_count)) handler(page);}#elsestatic inline struct metapage *page_to_mp(struct page *page, uint offset){ return PagePrivate(page) ? (struct metapage *)page_private(page) : NULL;}static inline int insert_metapage(struct page *page, struct metapage *mp){ if (mp) { set_page_private(page, (unsigned long)mp); SetPagePrivate(page); kmap(page); } return 0;}static inline void remove_metapage(struct page *page, struct metapage *mp){ set_page_private(page, 0); ClearPagePrivate(page); kunmap(page);}#define inc_io(page) do {} while(0)#define dec_io(page, handler) handler(page)#endifstatic void init_once(struct kmem_cache *cachep, void *foo){ struct metapage *mp = (struct metapage *)foo; 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(gfp_t 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); if (metapage_cache == NULL) return -ENOMEM; metapage_mempool = mempool_create_slab_pool(METAPOOL_MIN_PAGES, metapage_cache); if (metapage_mempool == NULL) { kmem_cache_destroy(metapage_cache); return -ENOMEM; } return 0;}void metapage_exit(void){ mempool_destroy(metapage_mempool); kmem_cache_destroy(metapage_cache);}static inline void drop_metapage(struct page *page, struct metapage *mp){ if (mp->count || mp->nohomeok || test_bit(META_dirty, &mp->flag) || test_bit(META_io, &mp->flag)) return; remove_metapage(page, mp); INCREMENT(mpStat.pagefree); free_metapage(mp);}/* * Metapage address space operations */static sector_t metapage_get_blocks(struct inode *inode, sector_t lblock, unsigned int *len){ int rc = 0; int xflag; s64 xaddr; sector_t file_blocks = (inode->i_size + inode->i_sb->s_blocksize - 1) >> inode->i_blkbits; if (lblock >= file_blocks) return 0; if (lblock + *len > file_blocks) *len = file_blocks - lblock; if (inode->i_ino) { rc = xtLookup(inode, (s64)lblock, *len, &xflag, &xaddr, len, 0); if ((rc == 0) && *len) lblock = (sector_t)xaddr; else lblock = 0; } /* else no mapping */ return lblock;}static void last_read_complete(struct page *page){ if (!PageError(page)) SetPageUptodate(page); unlock_page(page);}static void metapage_read_end_io(struct bio *bio, int err){ struct page *page = bio->bi_private; if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) { printk(KERN_ERR "metapage_read_end_io: I/O error\n"); SetPageError(page); } dec_io(page, last_read_complete); bio_put(bio);}static void remove_from_logsync(struct metapage *mp){ struct jfs_log *log = mp->log; unsigned long flags;/* * This can race. Recheck that log hasn't been set to null, and after * acquiring logsync lock, recheck lsn */ if (!log) return; LOGSYNC_LOCK(log, flags); if (mp->lsn) { mp->log = NULL; mp->lsn = 0; mp->clsn = 0; log->count--; list_del(&mp->synclist); } LOGSYNC_UNLOCK(log, flags);}static void last_write_complete(struct page *page){ struct metapage *mp; unsigned int offset; for (offset = 0; offset < PAGE_CACHE_SIZE; offset += PSIZE) { mp = page_to_mp(page, offset); if (mp && test_bit(META_io, &mp->flag)) { if (mp->lsn) remove_from_logsync(mp); clear_bit(META_io, &mp->flag); } /* * I'd like to call drop_metapage here, but I don't think it's * safe unless I have the page locked */ } end_page_writeback(page);}static void metapage_write_end_io(struct bio *bio, int err){ struct page *page = bio->bi_private; BUG_ON(!PagePrivate(page)); 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);}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 */ struct inode *inode = page->mapping->host; unsigned int blocks_per_mp = JFS_SBI(inode->i_sb)->nbperpage; unsigned int len; unsigned int xlen; struct metapage *mp; int redirty = 0; sector_t lblock; 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; page_start = (sector_t)page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits); BUG_ON(!PageLocked(page)); BUG_ON(PageWriteback(page)); for (offset = 0; offset < PAGE_CACHE_SIZE; offset += PSIZE) { mp = page_to_mp(page, offset); if (!mp || !test_bit(META_dirty, &mp->flag)) continue; 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); block_offset = offset >> inode->i_blkbits; lblock = page_start + block_offset; if (bio) { if (xlen && lblock == next_block) { /* Contiguous, in memory & on disk */ len = min(xlen, blocks_per_mp); xlen -= len; bio_bytes += len << inode->i_blkbits; set_bit(META_io, &mp->flag); continue; } /* Not contiguous */ if (bio_add_page(bio, page, bio_bytes, bio_offset) < bio_bytes) goto add_failed; /* * Increment counter before submitting i/o to keep * count from hitting zero before we're through */ inc_io(page); if (!bio->bi_size) goto dump_bio; submit_bio(WRITE, bio); bio = NULL; } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -