| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 2 | *   Copyright (C) International Business Machines Corp., 2000-2005 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3 | *   Portions Copyright (C) Christoph Hellwig, 2001-2002 | 
|  | 4 | * | 
|  | 5 | *   This program is free software;  you can redistribute it and/or modify | 
|  | 6 | *   it under the terms of the GNU General Public License as published by | 
|  | 7 | *   the Free Software Foundation; either version 2 of the License, or | 
|  | 8 | *   (at your option) any later version. | 
|  | 9 | * | 
|  | 10 | *   This program is distributed in the hope that it will be useful, | 
|  | 11 | *   but WITHOUT ANY WARRANTY;  without even the implied warranty of | 
|  | 12 | *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See | 
|  | 13 | *   the GNU General Public License for more details. | 
|  | 14 | * | 
|  | 15 | *   You should have received a copy of the GNU General Public License | 
|  | 16 | *   along with this program;  if not, write to the Free Software | 
|  | 17 | *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 
|  | 18 | */ | 
|  | 19 |  | 
|  | 20 | #include <linux/fs.h> | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 21 | #include <linux/mm.h> | 
|  | 22 | #include <linux/bio.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 23 | #include <linux/init.h> | 
|  | 24 | #include <linux/buffer_head.h> | 
|  | 25 | #include <linux/mempool.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 26 | #include "jfs_incore.h" | 
|  | 27 | #include "jfs_superblock.h" | 
|  | 28 | #include "jfs_filsys.h" | 
|  | 29 | #include "jfs_metapage.h" | 
|  | 30 | #include "jfs_txnmgr.h" | 
|  | 31 | #include "jfs_debug.h" | 
|  | 32 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 33 | #ifdef CONFIG_JFS_STATISTICS | 
|  | 34 | static struct { | 
|  | 35 | uint	pagealloc;	/* # of page allocations */ | 
|  | 36 | uint	pagefree;	/* # of page frees */ | 
|  | 37 | uint	lockwait;	/* # of sleeping lock_metapage() calls */ | 
|  | 38 | } mpStat; | 
|  | 39 | #endif | 
|  | 40 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 41 | #define metapage_locked(mp) test_bit(META_locked, &(mp)->flag) | 
|  | 42 | #define trylock_metapage(mp) test_and_set_bit(META_locked, &(mp)->flag) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 43 |  | 
|  | 44 | static inline void unlock_metapage(struct metapage *mp) | 
|  | 45 | { | 
|  | 46 | clear_bit(META_locked, &mp->flag); | 
|  | 47 | wake_up(&mp->wait); | 
|  | 48 | } | 
|  | 49 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 50 | static inline void __lock_metapage(struct metapage *mp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 51 | { | 
|  | 52 | DECLARE_WAITQUEUE(wait, current); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 53 | INCREMENT(mpStat.lockwait); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 54 | add_wait_queue_exclusive(&mp->wait, &wait); | 
|  | 55 | do { | 
|  | 56 | set_current_state(TASK_UNINTERRUPTIBLE); | 
|  | 57 | if (metapage_locked(mp)) { | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 58 | unlock_page(mp->page); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 59 | schedule(); | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 60 | lock_page(mp->page); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 61 | } | 
|  | 62 | } while (trylock_metapage(mp)); | 
|  | 63 | __set_current_state(TASK_RUNNING); | 
|  | 64 | remove_wait_queue(&mp->wait, &wait); | 
|  | 65 | } | 
|  | 66 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 67 | /* | 
|  | 68 | * Must have mp->page locked | 
|  | 69 | */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 70 | static inline void lock_metapage(struct metapage *mp) | 
|  | 71 | { | 
|  | 72 | if (trylock_metapage(mp)) | 
|  | 73 | __lock_metapage(mp); | 
|  | 74 | } | 
|  | 75 |  | 
|  | 76 | #define METAPOOL_MIN_PAGES 32 | 
|  | 77 | static kmem_cache_t *metapage_cache; | 
|  | 78 | static mempool_t *metapage_mempool; | 
|  | 79 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 80 | #define MPS_PER_PAGE (PAGE_CACHE_SIZE >> L2PSIZE) | 
|  | 81 |  | 
|  | 82 | #if MPS_PER_PAGE > 1 | 
|  | 83 |  | 
|  | 84 | struct meta_anchor { | 
|  | 85 | int mp_count; | 
|  | 86 | atomic_t io_count; | 
|  | 87 | struct metapage *mp[MPS_PER_PAGE]; | 
|  | 88 | }; | 
|  | 89 | #define mp_anchor(page) ((struct meta_anchor *)page->private) | 
|  | 90 |  | 
|  | 91 | static inline struct metapage *page_to_mp(struct page *page, uint offset) | 
|  | 92 | { | 
|  | 93 | if (!PagePrivate(page)) | 
|  | 94 | return NULL; | 
|  | 95 | return mp_anchor(page)->mp[offset >> L2PSIZE]; | 
|  | 96 | } | 
|  | 97 |  | 
|  | 98 | static inline int insert_metapage(struct page *page, struct metapage *mp) | 
|  | 99 | { | 
|  | 100 | struct meta_anchor *a; | 
|  | 101 | int index; | 
|  | 102 | int l2mp_blocks;	/* log2 blocks per metapage */ | 
|  | 103 |  | 
|  | 104 | if (PagePrivate(page)) | 
|  | 105 | a = mp_anchor(page); | 
|  | 106 | else { | 
|  | 107 | a = kmalloc(sizeof(struct meta_anchor), GFP_NOFS); | 
|  | 108 | if (!a) | 
|  | 109 | return -ENOMEM; | 
|  | 110 | memset(a, 0, sizeof(struct meta_anchor)); | 
|  | 111 | page->private = (unsigned long)a; | 
|  | 112 | SetPagePrivate(page); | 
|  | 113 | kmap(page); | 
|  | 114 | } | 
|  | 115 |  | 
|  | 116 | if (mp) { | 
|  | 117 | l2mp_blocks = L2PSIZE - page->mapping->host->i_blkbits; | 
|  | 118 | index = (mp->index >> l2mp_blocks) & (MPS_PER_PAGE - 1); | 
|  | 119 | a->mp_count++; | 
|  | 120 | a->mp[index] = mp; | 
|  | 121 | } | 
|  | 122 |  | 
|  | 123 | return 0; | 
|  | 124 | } | 
|  | 125 |  | 
|  | 126 | static inline void remove_metapage(struct page *page, struct metapage *mp) | 
|  | 127 | { | 
|  | 128 | struct meta_anchor *a = mp_anchor(page); | 
|  | 129 | int l2mp_blocks = L2PSIZE - page->mapping->host->i_blkbits; | 
|  | 130 | int index; | 
|  | 131 |  | 
|  | 132 | index = (mp->index >> l2mp_blocks) & (MPS_PER_PAGE - 1); | 
|  | 133 |  | 
|  | 134 | BUG_ON(a->mp[index] != mp); | 
|  | 135 |  | 
|  | 136 | a->mp[index] = NULL; | 
|  | 137 | if (--a->mp_count == 0) { | 
|  | 138 | kfree(a); | 
|  | 139 | page->private = 0; | 
|  | 140 | ClearPagePrivate(page); | 
|  | 141 | kunmap(page); | 
|  | 142 | } | 
|  | 143 | } | 
|  | 144 |  | 
|  | 145 | static inline void inc_io(struct page *page) | 
|  | 146 | { | 
|  | 147 | atomic_inc(&mp_anchor(page)->io_count); | 
|  | 148 | } | 
|  | 149 |  | 
|  | 150 | static inline void dec_io(struct page *page, void (*handler) (struct page *)) | 
|  | 151 | { | 
|  | 152 | if (atomic_dec_and_test(&mp_anchor(page)->io_count)) | 
|  | 153 | handler(page); | 
|  | 154 | } | 
|  | 155 |  | 
|  | 156 | #else | 
|  | 157 | static inline struct metapage *page_to_mp(struct page *page, uint offset) | 
|  | 158 | { | 
|  | 159 | return PagePrivate(page) ? (struct metapage *)page->private : NULL; | 
|  | 160 | } | 
|  | 161 |  | 
|  | 162 | static inline int insert_metapage(struct page *page, struct metapage *mp) | 
|  | 163 | { | 
|  | 164 | if (mp) { | 
|  | 165 | page->private = (unsigned long)mp; | 
|  | 166 | SetPagePrivate(page); | 
|  | 167 | kmap(page); | 
|  | 168 | } | 
|  | 169 | return 0; | 
|  | 170 | } | 
|  | 171 |  | 
|  | 172 | static inline void remove_metapage(struct page *page, struct metapage *mp) | 
|  | 173 | { | 
|  | 174 | page->private = 0; | 
|  | 175 | ClearPagePrivate(page); | 
|  | 176 | kunmap(page); | 
|  | 177 | } | 
|  | 178 |  | 
|  | 179 | #define inc_io(page) do {} while(0) | 
|  | 180 | #define dec_io(page, handler) handler(page) | 
|  | 181 |  | 
|  | 182 | #endif | 
|  | 183 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 184 | static void init_once(void *foo, kmem_cache_t *cachep, unsigned long flags) | 
|  | 185 | { | 
|  | 186 | struct metapage *mp = (struct metapage *)foo; | 
|  | 187 |  | 
|  | 188 | if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == | 
|  | 189 | SLAB_CTOR_CONSTRUCTOR) { | 
|  | 190 | mp->lid = 0; | 
|  | 191 | mp->lsn = 0; | 
|  | 192 | mp->flag = 0; | 
|  | 193 | mp->data = NULL; | 
|  | 194 | mp->clsn = 0; | 
|  | 195 | mp->log = NULL; | 
|  | 196 | set_bit(META_free, &mp->flag); | 
|  | 197 | init_waitqueue_head(&mp->wait); | 
|  | 198 | } | 
|  | 199 | } | 
|  | 200 |  | 
| Al Viro | 27496a8 | 2005-10-21 03:20:48 -0400 | [diff] [blame] | 201 | static inline struct metapage *alloc_metapage(gfp_t gfp_mask) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 202 | { | 
|  | 203 | return mempool_alloc(metapage_mempool, gfp_mask); | 
|  | 204 | } | 
|  | 205 |  | 
|  | 206 | static inline void free_metapage(struct metapage *mp) | 
|  | 207 | { | 
|  | 208 | mp->flag = 0; | 
|  | 209 | set_bit(META_free, &mp->flag); | 
|  | 210 |  | 
|  | 211 | mempool_free(mp, metapage_mempool); | 
|  | 212 | } | 
|  | 213 |  | 
|  | 214 | int __init metapage_init(void) | 
|  | 215 | { | 
|  | 216 | /* | 
|  | 217 | * Allocate the metapage structures | 
|  | 218 | */ | 
|  | 219 | metapage_cache = kmem_cache_create("jfs_mp", sizeof(struct metapage), | 
|  | 220 | 0, 0, init_once, NULL); | 
|  | 221 | if (metapage_cache == NULL) | 
|  | 222 | return -ENOMEM; | 
|  | 223 |  | 
|  | 224 | metapage_mempool = mempool_create(METAPOOL_MIN_PAGES, mempool_alloc_slab, | 
|  | 225 | mempool_free_slab, metapage_cache); | 
|  | 226 |  | 
|  | 227 | if (metapage_mempool == NULL) { | 
|  | 228 | kmem_cache_destroy(metapage_cache); | 
|  | 229 | return -ENOMEM; | 
|  | 230 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 231 |  | 
|  | 232 | return 0; | 
|  | 233 | } | 
|  | 234 |  | 
|  | 235 | void metapage_exit(void) | 
|  | 236 | { | 
|  | 237 | mempool_destroy(metapage_mempool); | 
|  | 238 | kmem_cache_destroy(metapage_cache); | 
|  | 239 | } | 
|  | 240 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 241 | static inline void drop_metapage(struct page *page, struct metapage *mp) | 
|  | 242 | { | 
|  | 243 | if (mp->count || mp->nohomeok || test_bit(META_dirty, &mp->flag) || | 
|  | 244 | test_bit(META_io, &mp->flag)) | 
|  | 245 | return; | 
|  | 246 | remove_metapage(page, mp); | 
|  | 247 | INCREMENT(mpStat.pagefree); | 
|  | 248 | free_metapage(mp); | 
|  | 249 | } | 
|  | 250 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 251 | /* | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 252 | * Metapage address space operations | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 253 | */ | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 254 |  | 
|  | 255 | static sector_t metapage_get_blocks(struct inode *inode, sector_t lblock, | 
|  | 256 | unsigned int *len) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 257 | { | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 258 | int rc = 0; | 
|  | 259 | int xflag; | 
|  | 260 | s64 xaddr; | 
|  | 261 | sector_t file_blocks = (inode->i_size + inode->i_blksize - 1) >> | 
|  | 262 | inode->i_blkbits; | 
|  | 263 |  | 
|  | 264 | if (lblock >= file_blocks) | 
|  | 265 | return 0; | 
|  | 266 | if (lblock + *len > file_blocks) | 
|  | 267 | *len = file_blocks - lblock; | 
|  | 268 |  | 
|  | 269 | if (inode->i_ino) { | 
|  | 270 | rc = xtLookup(inode, (s64)lblock, *len, &xflag, &xaddr, len, 0); | 
|  | 271 | if ((rc == 0) && *len) | 
|  | 272 | lblock = (sector_t)xaddr; | 
|  | 273 | else | 
|  | 274 | lblock = 0; | 
|  | 275 | } /* else no mapping */ | 
|  | 276 |  | 
|  | 277 | return lblock; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 278 | } | 
|  | 279 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 280 | static void last_read_complete(struct page *page) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 281 | { | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 282 | if (!PageError(page)) | 
|  | 283 | SetPageUptodate(page); | 
|  | 284 | unlock_page(page); | 
|  | 285 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 286 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 287 | static int metapage_read_end_io(struct bio *bio, unsigned int bytes_done, | 
|  | 288 | int err) | 
|  | 289 | { | 
|  | 290 | struct page *page = bio->bi_private; | 
|  | 291 |  | 
|  | 292 | if (bio->bi_size) | 
|  | 293 | return 1; | 
|  | 294 |  | 
|  | 295 | if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) { | 
|  | 296 | printk(KERN_ERR "metapage_read_end_io: I/O error\n"); | 
|  | 297 | SetPageError(page); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 298 | } | 
|  | 299 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 300 | dec_io(page, last_read_complete); | 
|  | 301 | bio_put(bio); | 
|  | 302 |  | 
|  | 303 | return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 304 | } | 
|  | 305 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 306 | static void remove_from_logsync(struct metapage *mp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 307 | { | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 308 | struct jfs_log *log = mp->log; | 
|  | 309 | unsigned long flags; | 
|  | 310 | /* | 
|  | 311 | * This can race.  Recheck that log hasn't been set to null, and after | 
|  | 312 | * acquiring logsync lock, recheck lsn | 
|  | 313 | */ | 
|  | 314 | if (!log) | 
|  | 315 | return; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 316 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 317 | LOGSYNC_LOCK(log, flags); | 
|  | 318 | if (mp->lsn) { | 
|  | 319 | mp->log = NULL; | 
|  | 320 | mp->lsn = 0; | 
|  | 321 | mp->clsn = 0; | 
|  | 322 | log->count--; | 
|  | 323 | list_del(&mp->synclist); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 324 | } | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 325 | LOGSYNC_UNLOCK(log, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 326 | } | 
|  | 327 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 328 | static void last_write_complete(struct page *page) | 
|  | 329 | { | 
|  | 330 | struct metapage *mp; | 
|  | 331 | unsigned int offset; | 
|  | 332 |  | 
|  | 333 | for (offset = 0; offset < PAGE_CACHE_SIZE; offset += PSIZE) { | 
|  | 334 | mp = page_to_mp(page, offset); | 
|  | 335 | if (mp && test_bit(META_io, &mp->flag)) { | 
|  | 336 | if (mp->lsn) | 
|  | 337 | remove_from_logsync(mp); | 
|  | 338 | clear_bit(META_io, &mp->flag); | 
|  | 339 | } | 
|  | 340 | /* | 
|  | 341 | * I'd like to call drop_metapage here, but I don't think it's | 
|  | 342 | * safe unless I have the page locked | 
|  | 343 | */ | 
|  | 344 | } | 
|  | 345 | end_page_writeback(page); | 
|  | 346 | } | 
|  | 347 |  | 
|  | 348 | static int metapage_write_end_io(struct bio *bio, unsigned int bytes_done, | 
|  | 349 | int err) | 
|  | 350 | { | 
|  | 351 | struct page *page = bio->bi_private; | 
|  | 352 |  | 
|  | 353 | BUG_ON(!PagePrivate(page)); | 
|  | 354 |  | 
|  | 355 | if (bio->bi_size) | 
|  | 356 | return 1; | 
|  | 357 |  | 
|  | 358 | if (! test_bit(BIO_UPTODATE, &bio->bi_flags)) { | 
|  | 359 | printk(KERN_ERR "metapage_write_end_io: I/O error\n"); | 
|  | 360 | SetPageError(page); | 
|  | 361 | } | 
|  | 362 | dec_io(page, last_write_complete); | 
|  | 363 | bio_put(bio); | 
|  | 364 | return 0; | 
|  | 365 | } | 
|  | 366 |  | 
|  | 367 | static int metapage_writepage(struct page *page, struct writeback_control *wbc) | 
|  | 368 | { | 
|  | 369 | struct bio *bio = NULL; | 
|  | 370 | unsigned int block_offset;	/* block offset of mp within page */ | 
|  | 371 | struct inode *inode = page->mapping->host; | 
|  | 372 | unsigned int blocks_per_mp = JFS_SBI(inode->i_sb)->nbperpage; | 
|  | 373 | unsigned int len; | 
|  | 374 | unsigned int xlen; | 
|  | 375 | struct metapage *mp; | 
|  | 376 | int redirty = 0; | 
|  | 377 | sector_t lblock; | 
|  | 378 | sector_t pblock; | 
|  | 379 | sector_t next_block = 0; | 
|  | 380 | sector_t page_start; | 
|  | 381 | unsigned long bio_bytes = 0; | 
|  | 382 | unsigned long bio_offset = 0; | 
|  | 383 | unsigned int offset; | 
|  | 384 |  | 
|  | 385 | page_start = (sector_t)page->index << | 
|  | 386 | (PAGE_CACHE_SHIFT - inode->i_blkbits); | 
|  | 387 | BUG_ON(!PageLocked(page)); | 
|  | 388 | BUG_ON(PageWriteback(page)); | 
|  | 389 |  | 
|  | 390 | for (offset = 0; offset < PAGE_CACHE_SIZE; offset += PSIZE) { | 
|  | 391 | mp = page_to_mp(page, offset); | 
|  | 392 |  | 
|  | 393 | if (!mp || !test_bit(META_dirty, &mp->flag)) | 
|  | 394 | continue; | 
|  | 395 |  | 
|  | 396 | if (mp->nohomeok && !test_bit(META_forcewrite, &mp->flag)) { | 
|  | 397 | redirty = 1; | 
|  | 398 | continue; | 
|  | 399 | } | 
|  | 400 |  | 
|  | 401 | clear_bit(META_dirty, &mp->flag); | 
|  | 402 | block_offset = offset >> inode->i_blkbits; | 
|  | 403 | lblock = page_start + block_offset; | 
|  | 404 | if (bio) { | 
|  | 405 | if (xlen && lblock == next_block) { | 
|  | 406 | /* Contiguous, in memory & on disk */ | 
|  | 407 | len = min(xlen, blocks_per_mp); | 
|  | 408 | xlen -= len; | 
|  | 409 | bio_bytes += len << inode->i_blkbits; | 
|  | 410 | set_bit(META_io, &mp->flag); | 
|  | 411 | continue; | 
|  | 412 | } | 
|  | 413 | /* Not contiguous */ | 
|  | 414 | if (bio_add_page(bio, page, bio_bytes, bio_offset) < | 
|  | 415 | bio_bytes) | 
|  | 416 | goto add_failed; | 
|  | 417 | /* | 
|  | 418 | * Increment counter before submitting i/o to keep | 
|  | 419 | * count from hitting zero before we're through | 
|  | 420 | */ | 
|  | 421 | inc_io(page); | 
|  | 422 | if (!bio->bi_size) | 
|  | 423 | goto dump_bio; | 
|  | 424 | submit_bio(WRITE, bio); | 
|  | 425 | bio = NULL; | 
|  | 426 | } else { | 
|  | 427 | set_page_writeback(page); | 
|  | 428 | inc_io(page); | 
|  | 429 | } | 
|  | 430 | xlen = (PAGE_CACHE_SIZE - offset) >> inode->i_blkbits; | 
|  | 431 | pblock = metapage_get_blocks(inode, lblock, &xlen); | 
|  | 432 | if (!pblock) { | 
|  | 433 | /* Need better error handling */ | 
|  | 434 | printk(KERN_ERR "JFS: metapage_get_blocks failed\n"); | 
|  | 435 | dec_io(page, last_write_complete); | 
|  | 436 | continue; | 
|  | 437 | } | 
|  | 438 | set_bit(META_io, &mp->flag); | 
|  | 439 | len = min(xlen, (uint) JFS_SBI(inode->i_sb)->nbperpage); | 
|  | 440 |  | 
|  | 441 | bio = bio_alloc(GFP_NOFS, 1); | 
|  | 442 | bio->bi_bdev = inode->i_sb->s_bdev; | 
|  | 443 | bio->bi_sector = pblock << (inode->i_blkbits - 9); | 
|  | 444 | bio->bi_end_io = metapage_write_end_io; | 
|  | 445 | bio->bi_private = page; | 
|  | 446 |  | 
|  | 447 | /* Don't call bio_add_page yet, we may add to this vec */ | 
|  | 448 | bio_offset = offset; | 
|  | 449 | bio_bytes = len << inode->i_blkbits; | 
|  | 450 |  | 
|  | 451 | xlen -= len; | 
|  | 452 | next_block = lblock + len; | 
|  | 453 | } | 
|  | 454 | if (bio) { | 
|  | 455 | if (bio_add_page(bio, page, bio_bytes, bio_offset) < bio_bytes) | 
|  | 456 | goto add_failed; | 
|  | 457 | if (!bio->bi_size) | 
|  | 458 | goto dump_bio; | 
|  | 459 |  | 
|  | 460 | submit_bio(WRITE, bio); | 
|  | 461 | } | 
|  | 462 | if (redirty) | 
|  | 463 | redirty_page_for_writepage(wbc, page); | 
|  | 464 |  | 
|  | 465 | unlock_page(page); | 
|  | 466 |  | 
|  | 467 | return 0; | 
|  | 468 | add_failed: | 
|  | 469 | /* We should never reach here, since we're only adding one vec */ | 
|  | 470 | printk(KERN_ERR "JFS: bio_add_page failed unexpectedly\n"); | 
|  | 471 | goto skip; | 
|  | 472 | dump_bio: | 
|  | 473 | dump_mem("bio", bio, sizeof(*bio)); | 
|  | 474 | skip: | 
|  | 475 | bio_put(bio); | 
|  | 476 | unlock_page(page); | 
|  | 477 | dec_io(page, last_write_complete); | 
|  | 478 |  | 
|  | 479 | return -EIO; | 
|  | 480 | } | 
|  | 481 |  | 
|  | 482 | static int metapage_readpage(struct file *fp, struct page *page) | 
|  | 483 | { | 
|  | 484 | struct inode *inode = page->mapping->host; | 
|  | 485 | struct bio *bio = NULL; | 
|  | 486 | unsigned int block_offset; | 
|  | 487 | unsigned int blocks_per_page = PAGE_CACHE_SIZE >> inode->i_blkbits; | 
|  | 488 | sector_t page_start;	/* address of page in fs blocks */ | 
|  | 489 | sector_t pblock; | 
|  | 490 | unsigned int xlen; | 
|  | 491 | unsigned int len; | 
|  | 492 | unsigned int offset; | 
|  | 493 |  | 
|  | 494 | BUG_ON(!PageLocked(page)); | 
|  | 495 | page_start = (sector_t)page->index << | 
|  | 496 | (PAGE_CACHE_SHIFT - inode->i_blkbits); | 
|  | 497 |  | 
|  | 498 | block_offset = 0; | 
|  | 499 | while (block_offset < blocks_per_page) { | 
|  | 500 | xlen = blocks_per_page - block_offset; | 
|  | 501 | pblock = metapage_get_blocks(inode, page_start + block_offset, | 
|  | 502 | &xlen); | 
|  | 503 | if (pblock) { | 
|  | 504 | if (!PagePrivate(page)) | 
|  | 505 | insert_metapage(page, NULL); | 
|  | 506 | inc_io(page); | 
|  | 507 | if (bio) | 
|  | 508 | submit_bio(READ, bio); | 
|  | 509 |  | 
|  | 510 | bio = bio_alloc(GFP_NOFS, 1); | 
|  | 511 | bio->bi_bdev = inode->i_sb->s_bdev; | 
|  | 512 | bio->bi_sector = pblock << (inode->i_blkbits - 9); | 
|  | 513 | bio->bi_end_io = metapage_read_end_io; | 
|  | 514 | bio->bi_private = page; | 
|  | 515 | len = xlen << inode->i_blkbits; | 
|  | 516 | offset = block_offset << inode->i_blkbits; | 
|  | 517 | if (bio_add_page(bio, page, len, offset) < len) | 
|  | 518 | goto add_failed; | 
|  | 519 | block_offset += xlen; | 
|  | 520 | } else | 
|  | 521 | block_offset++; | 
|  | 522 | } | 
|  | 523 | if (bio) | 
|  | 524 | submit_bio(READ, bio); | 
|  | 525 | else | 
|  | 526 | unlock_page(page); | 
|  | 527 |  | 
|  | 528 | return 0; | 
|  | 529 |  | 
|  | 530 | add_failed: | 
|  | 531 | printk(KERN_ERR "JFS: bio_add_page failed unexpectedly\n"); | 
|  | 532 | bio_put(bio); | 
|  | 533 | dec_io(page, last_read_complete); | 
|  | 534 | return -EIO; | 
|  | 535 | } | 
|  | 536 |  | 
| Al Viro | 27496a8 | 2005-10-21 03:20:48 -0400 | [diff] [blame] | 537 | static int metapage_releasepage(struct page *page, gfp_t gfp_mask) | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 538 | { | 
|  | 539 | struct metapage *mp; | 
|  | 540 | int busy = 0; | 
|  | 541 | unsigned int offset; | 
|  | 542 |  | 
|  | 543 | for (offset = 0; offset < PAGE_CACHE_SIZE; offset += PSIZE) { | 
|  | 544 | mp = page_to_mp(page, offset); | 
|  | 545 |  | 
|  | 546 | if (!mp) | 
|  | 547 | continue; | 
|  | 548 |  | 
|  | 549 | jfs_info("metapage_releasepage: mp = 0x%p", mp); | 
|  | 550 | if (mp->count || mp->nohomeok) { | 
|  | 551 | jfs_info("count = %ld, nohomeok = %d", mp->count, | 
|  | 552 | mp->nohomeok); | 
|  | 553 | busy = 1; | 
|  | 554 | continue; | 
|  | 555 | } | 
|  | 556 | wait_on_page_writeback(page); | 
|  | 557 | //WARN_ON(test_bit(META_dirty, &mp->flag)); | 
|  | 558 | if (test_bit(META_dirty, &mp->flag)) { | 
|  | 559 | dump_mem("dirty mp in metapage_releasepage", mp, | 
|  | 560 | sizeof(struct metapage)); | 
|  | 561 | dump_mem("page", page, sizeof(struct page)); | 
|  | 562 | dump_stack(); | 
|  | 563 | } | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 564 | if (mp->lsn) | 
|  | 565 | remove_from_logsync(mp); | 
|  | 566 | remove_metapage(page, mp); | 
|  | 567 | INCREMENT(mpStat.pagefree); | 
|  | 568 | free_metapage(mp); | 
|  | 569 | } | 
|  | 570 | if (busy) | 
|  | 571 | return -1; | 
|  | 572 |  | 
|  | 573 | return 0; | 
|  | 574 | } | 
|  | 575 |  | 
|  | 576 | static int metapage_invalidatepage(struct page *page, unsigned long offset) | 
|  | 577 | { | 
|  | 578 | BUG_ON(offset); | 
|  | 579 |  | 
|  | 580 | if (PageWriteback(page)) | 
|  | 581 | return 0; | 
|  | 582 |  | 
|  | 583 | return metapage_releasepage(page, 0); | 
|  | 584 | } | 
|  | 585 |  | 
|  | 586 | struct address_space_operations jfs_metapage_aops = { | 
|  | 587 | .readpage	= metapage_readpage, | 
|  | 588 | .writepage	= metapage_writepage, | 
|  | 589 | .sync_page	= block_sync_page, | 
|  | 590 | .releasepage	= metapage_releasepage, | 
|  | 591 | .invalidatepage	= metapage_invalidatepage, | 
|  | 592 | .set_page_dirty	= __set_page_dirty_nobuffers, | 
|  | 593 | }; | 
|  | 594 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 595 | struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, | 
|  | 596 | unsigned int size, int absolute, | 
|  | 597 | unsigned long new) | 
|  | 598 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 599 | int l2BlocksPerPage; | 
|  | 600 | int l2bsize; | 
|  | 601 | struct address_space *mapping; | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 602 | struct metapage *mp = NULL; | 
|  | 603 | struct page *page; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 604 | unsigned long page_index; | 
|  | 605 | unsigned long page_offset; | 
|  | 606 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 607 | jfs_info("__get_metapage: ino = %ld, lblock = 0x%lx, abs=%d", | 
|  | 608 | inode->i_ino, lblock, absolute); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 609 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 610 | l2bsize = inode->i_blkbits; | 
|  | 611 | l2BlocksPerPage = PAGE_CACHE_SHIFT - l2bsize; | 
|  | 612 | page_index = lblock >> l2BlocksPerPage; | 
|  | 613 | page_offset = (lblock - (page_index << l2BlocksPerPage)) << l2bsize; | 
|  | 614 | if ((page_offset + size) > PAGE_CACHE_SIZE) { | 
|  | 615 | jfs_err("MetaData crosses page boundary!!"); | 
|  | 616 | jfs_err("lblock = %lx, size  = %d", lblock, size); | 
|  | 617 | dump_stack(); | 
|  | 618 | return NULL; | 
|  | 619 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 620 | if (absolute) | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 621 | mapping = JFS_SBI(inode->i_sb)->direct_inode->i_mapping; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 622 | else { | 
|  | 623 | /* | 
|  | 624 | * If an nfs client tries to read an inode that is larger | 
|  | 625 | * than any existing inodes, we may try to read past the | 
|  | 626 | * end of the inode map | 
|  | 627 | */ | 
|  | 628 | if ((lblock << inode->i_blkbits) >= inode->i_size) | 
|  | 629 | return NULL; | 
|  | 630 | mapping = inode->i_mapping; | 
|  | 631 | } | 
|  | 632 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 633 | if (new && (PSIZE == PAGE_CACHE_SIZE)) { | 
|  | 634 | page = grab_cache_page(mapping, page_index); | 
|  | 635 | if (!page) { | 
|  | 636 | jfs_err("grab_cache_page failed!"); | 
|  | 637 | return NULL; | 
|  | 638 | } | 
|  | 639 | SetPageUptodate(page); | 
|  | 640 | } else { | 
|  | 641 | page = read_cache_page(mapping, page_index, | 
|  | 642 | (filler_t *)mapping->a_ops->readpage, NULL); | 
| Dave Kleikamp | c40c2024 | 2005-07-22 11:08:44 -0500 | [diff] [blame] | 643 | if (IS_ERR(page) || !PageUptodate(page)) { | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 644 | jfs_err("read_cache_page failed!"); | 
|  | 645 | return NULL; | 
|  | 646 | } | 
|  | 647 | lock_page(page); | 
|  | 648 | } | 
|  | 649 |  | 
|  | 650 | mp = page_to_mp(page, page_offset); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 651 | if (mp) { | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 652 | if (mp->logical_size != size) { | 
|  | 653 | jfs_error(inode->i_sb, | 
|  | 654 | "__get_metapage: mp->logical_size != size"); | 
|  | 655 | jfs_err("logical_size = %d, size = %d", | 
|  | 656 | mp->logical_size, size); | 
|  | 657 | dump_stack(); | 
|  | 658 | goto unlock; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 659 | } | 
|  | 660 | mp->count++; | 
|  | 661 | lock_metapage(mp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 662 | if (test_bit(META_discard, &mp->flag)) { | 
|  | 663 | if (!new) { | 
|  | 664 | jfs_error(inode->i_sb, | 
|  | 665 | "__get_metapage: using a " | 
|  | 666 | "discarded metapage"); | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 667 | discard_metapage(mp); | 
|  | 668 | goto unlock; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 669 | } | 
|  | 670 | clear_bit(META_discard, &mp->flag); | 
|  | 671 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 672 | } else { | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 673 | INCREMENT(mpStat.pagealloc); | 
|  | 674 | mp = alloc_metapage(GFP_NOFS); | 
|  | 675 | mp->page = page; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 676 | mp->flag = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 677 | mp->xflag = COMMIT_PAGE; | 
|  | 678 | mp->count = 1; | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 679 | mp->nohomeok = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 680 | mp->logical_size = size; | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 681 | mp->data = page_address(page) + page_offset; | 
|  | 682 | mp->index = lblock; | 
|  | 683 | if (unlikely(insert_metapage(page, mp))) { | 
|  | 684 | free_metapage(mp); | 
|  | 685 | goto unlock; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 686 | } | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 687 | lock_metapage(mp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 688 | } | 
|  | 689 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 690 | if (new) { | 
|  | 691 | jfs_info("zeroing mp = 0x%p", mp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 692 | memset(mp->data, 0, PSIZE); | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 693 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 694 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 695 | unlock_page(page); | 
|  | 696 | jfs_info("__get_metapage: returning = 0x%p data = 0x%p", mp, mp->data); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 697 | return mp; | 
|  | 698 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 699 | unlock: | 
|  | 700 | unlock_page(page); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 701 | return NULL; | 
|  | 702 | } | 
|  | 703 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 704 | void grab_metapage(struct metapage * mp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 705 | { | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 706 | jfs_info("grab_metapage: mp = 0x%p", mp); | 
|  | 707 | page_cache_get(mp->page); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 708 | lock_page(mp->page); | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 709 | mp->count++; | 
|  | 710 | lock_metapage(mp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 711 | unlock_page(mp->page); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 712 | } | 
|  | 713 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 714 | void force_metapage(struct metapage *mp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 715 | { | 
|  | 716 | struct page *page = mp->page; | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 717 | jfs_info("force_metapage: mp = 0x%p", mp); | 
|  | 718 | set_bit(META_forcewrite, &mp->flag); | 
|  | 719 | clear_bit(META_sync, &mp->flag); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 720 | page_cache_get(page); | 
|  | 721 | lock_page(page); | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 722 | set_page_dirty(page); | 
|  | 723 | write_one_page(page, 1); | 
|  | 724 | clear_bit(META_forcewrite, &mp->flag); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 725 | page_cache_release(page); | 
|  | 726 | } | 
|  | 727 |  | 
| Dave Kleikamp | 1868f4a | 2005-05-04 15:29:35 -0500 | [diff] [blame] | 728 | void hold_metapage(struct metapage *mp) | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 729 | { | 
|  | 730 | lock_page(mp->page); | 
|  | 731 | } | 
|  | 732 |  | 
| Dave Kleikamp | 1868f4a | 2005-05-04 15:29:35 -0500 | [diff] [blame] | 733 | void put_metapage(struct metapage *mp) | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 734 | { | 
|  | 735 | if (mp->count || mp->nohomeok) { | 
|  | 736 | /* Someone else will release this */ | 
|  | 737 | unlock_page(mp->page); | 
|  | 738 | return; | 
|  | 739 | } | 
|  | 740 | page_cache_get(mp->page); | 
|  | 741 | mp->count++; | 
|  | 742 | lock_metapage(mp); | 
|  | 743 | unlock_page(mp->page); | 
|  | 744 | release_metapage(mp); | 
|  | 745 | } | 
|  | 746 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 747 | void release_metapage(struct metapage * mp) | 
|  | 748 | { | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 749 | struct page *page = mp->page; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 750 | jfs_info("release_metapage: mp = 0x%p, flag = 0x%lx", mp, mp->flag); | 
|  | 751 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 752 | BUG_ON(!page); | 
|  | 753 |  | 
|  | 754 | lock_page(page); | 
|  | 755 | unlock_metapage(mp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 756 |  | 
|  | 757 | assert(mp->count); | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 758 | if (--mp->count || mp->nohomeok) { | 
|  | 759 | unlock_page(page); | 
|  | 760 | page_cache_release(page); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 761 | return; | 
|  | 762 | } | 
|  | 763 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 764 | if (test_bit(META_dirty, &mp->flag)) { | 
|  | 765 | set_page_dirty(page); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 766 | if (test_bit(META_sync, &mp->flag)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 767 | clear_bit(META_sync, &mp->flag); | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 768 | write_one_page(page, 1); | 
|  | 769 | lock_page(page); /* write_one_page unlocks the page */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 770 | } | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 771 | } else if (mp->lsn)	/* discard_metapage doesn't remove it */ | 
|  | 772 | remove_from_logsync(mp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 773 |  | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 774 | #if MPS_PER_PAGE == 1 | 
|  | 775 | /* | 
|  | 776 | * If we know this is the only thing in the page, we can throw | 
|  | 777 | * the page out of the page cache.  If pages are larger, we | 
|  | 778 | * don't want to do this. | 
|  | 779 | */ | 
|  | 780 |  | 
|  | 781 | /* Retest mp->count since we may have released page lock */ | 
|  | 782 | if (test_bit(META_discard, &mp->flag) && !mp->count) { | 
|  | 783 | clear_page_dirty(page); | 
|  | 784 | ClearPageUptodate(page); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 785 | } | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 786 | #else | 
|  | 787 | /* Try to keep metapages from using up too much memory */ | 
|  | 788 | drop_metapage(page, mp); | 
|  | 789 | #endif | 
|  | 790 | unlock_page(page); | 
|  | 791 | page_cache_release(page); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 792 | } | 
|  | 793 |  | 
|  | 794 | void __invalidate_metapages(struct inode *ip, s64 addr, int len) | 
|  | 795 | { | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 796 | sector_t lblock; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 797 | int l2BlocksPerPage = PAGE_CACHE_SHIFT - ip->i_blkbits; | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 798 | int BlocksPerPage = 1 << l2BlocksPerPage; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 799 | /* All callers are interested in block device's mapping */ | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 800 | struct address_space *mapping = | 
|  | 801 | JFS_SBI(ip->i_sb)->direct_inode->i_mapping; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 802 | struct metapage *mp; | 
|  | 803 | struct page *page; | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 804 | unsigned int offset; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 805 |  | 
|  | 806 | /* | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 807 | * Mark metapages to discard.  They will eventually be | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 808 | * released, but should not be written. | 
|  | 809 | */ | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 810 | for (lblock = addr & ~(BlocksPerPage - 1); lblock < addr + len; | 
|  | 811 | lblock += BlocksPerPage) { | 
|  | 812 | page = find_lock_page(mapping, lblock >> l2BlocksPerPage); | 
|  | 813 | if (!page) | 
|  | 814 | continue; | 
|  | 815 | for (offset = 0; offset < PAGE_CACHE_SIZE; offset += PSIZE) { | 
|  | 816 | mp = page_to_mp(page, offset); | 
|  | 817 | if (!mp) | 
|  | 818 | continue; | 
|  | 819 | if (mp->index < addr) | 
|  | 820 | continue; | 
|  | 821 | if (mp->index >= addr + len) | 
|  | 822 | break; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 823 |  | 
|  | 824 | clear_bit(META_dirty, &mp->flag); | 
|  | 825 | set_bit(META_discard, &mp->flag); | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 826 | if (mp->lsn) | 
|  | 827 | remove_from_logsync(mp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 828 | } | 
| Dave Kleikamp | 7fab479 | 2005-05-02 12:25:02 -0600 | [diff] [blame] | 829 | unlock_page(page); | 
|  | 830 | page_cache_release(page); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 831 | } | 
|  | 832 | } | 
|  | 833 |  | 
|  | 834 | #ifdef CONFIG_JFS_STATISTICS | 
|  | 835 | int jfs_mpstat_read(char *buffer, char **start, off_t offset, int length, | 
|  | 836 | int *eof, void *data) | 
|  | 837 | { | 
|  | 838 | int len = 0; | 
|  | 839 | off_t begin; | 
|  | 840 |  | 
|  | 841 | len += sprintf(buffer, | 
|  | 842 | "JFS Metapage statistics\n" | 
|  | 843 | "=======================\n" | 
|  | 844 | "page allocations = %d\n" | 
|  | 845 | "page frees = %d\n" | 
|  | 846 | "lock waits = %d\n", | 
|  | 847 | mpStat.pagealloc, | 
|  | 848 | mpStat.pagefree, | 
|  | 849 | mpStat.lockwait); | 
|  | 850 |  | 
|  | 851 | begin = offset; | 
|  | 852 | *start = buffer + begin; | 
|  | 853 | len -= begin; | 
|  | 854 |  | 
|  | 855 | if (len > length) | 
|  | 856 | len = length; | 
|  | 857 | else | 
|  | 858 | *eof = 1; | 
|  | 859 |  | 
|  | 860 | if (len < 0) | 
|  | 861 | len = 0; | 
|  | 862 |  | 
|  | 863 | return len; | 
|  | 864 | } | 
|  | 865 | #endif |