linux/fs/nilfs2/btnode.c
<<
>>
Prefs
   1/*
   2 * btnode.c - NILFS B-tree node cache
   3 *
   4 * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program; if not, write to the Free Software
  18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  19 *
  20 * This file was originally written by Seiji Kihara <kihara@osrg.net>
  21 * and fully revised by Ryusuke Konishi <ryusuke@osrg.net> for
  22 * stabilization and simplification.
  23 *
  24 */
  25
  26#include <linux/types.h>
  27#include <linux/buffer_head.h>
  28#include <linux/mm.h>
  29#include <linux/backing-dev.h>
  30#include <linux/gfp.h>
  31#include "nilfs.h"
  32#include "mdt.h"
  33#include "dat.h"
  34#include "page.h"
  35#include "btnode.h"
  36
  37void nilfs_btnode_cache_clear(struct address_space *btnc)
  38{
  39        invalidate_mapping_pages(btnc, 0, -1);
  40        truncate_inode_pages(btnc, 0);
  41}
  42
  43struct buffer_head *
  44nilfs_btnode_create_block(struct address_space *btnc, __u64 blocknr)
  45{
  46        struct inode *inode = NILFS_BTNC_I(btnc);
  47        struct buffer_head *bh;
  48
  49        bh = nilfs_grab_buffer(inode, btnc, blocknr, 1 << BH_NILFS_Node);
  50        if (unlikely(!bh))
  51                return NULL;
  52
  53        if (unlikely(buffer_mapped(bh) || buffer_uptodate(bh) ||
  54                     buffer_dirty(bh))) {
  55                brelse(bh);
  56                BUG();
  57        }
  58        memset(bh->b_data, 0, 1 << inode->i_blkbits);
  59        bh->b_bdev = inode->i_sb->s_bdev;
  60        bh->b_blocknr = blocknr;
  61        set_buffer_mapped(bh);
  62        set_buffer_uptodate(bh);
  63
  64        unlock_page(bh->b_page);
  65        page_cache_release(bh->b_page);
  66        return bh;
  67}
  68
  69int nilfs_btnode_submit_block(struct address_space *btnc, __u64 blocknr,
  70                              sector_t pblocknr, int mode,
  71                              struct buffer_head **pbh, sector_t *submit_ptr)
  72{
  73        struct buffer_head *bh;
  74        struct inode *inode = NILFS_BTNC_I(btnc);
  75        struct page *page;
  76        int err;
  77
  78        bh = nilfs_grab_buffer(inode, btnc, blocknr, 1 << BH_NILFS_Node);
  79        if (unlikely(!bh))
  80                return -ENOMEM;
  81
  82        err = -EEXIST; /* internal code */
  83        page = bh->b_page;
  84
  85        if (buffer_uptodate(bh) || buffer_dirty(bh))
  86                goto found;
  87
  88        if (pblocknr == 0) {
  89                pblocknr = blocknr;
  90                if (inode->i_ino != NILFS_DAT_INO) {
  91                        struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
  92
  93                        /* blocknr is a virtual block number */
  94                        err = nilfs_dat_translate(nilfs->ns_dat, blocknr,
  95                                                  &pblocknr);
  96                        if (unlikely(err)) {
  97                                brelse(bh);
  98                                goto out_locked;
  99                        }
 100                }
 101        }
 102
 103        if (mode == READA) {
 104                if (pblocknr != *submit_ptr + 1 || !trylock_buffer(bh)) {
 105                        err = -EBUSY; /* internal code */
 106                        brelse(bh);
 107                        goto out_locked;
 108                }
 109        } else { /* mode == READ */
 110                lock_buffer(bh);
 111        }
 112        if (buffer_uptodate(bh)) {
 113                unlock_buffer(bh);
 114                err = -EEXIST; /* internal code */
 115                goto found;
 116        }
 117        set_buffer_mapped(bh);
 118        bh->b_bdev = inode->i_sb->s_bdev;
 119        bh->b_blocknr = pblocknr; /* set block address for read */
 120        bh->b_end_io = end_buffer_read_sync;
 121        get_bh(bh);
 122        submit_bh(mode, bh);
 123        bh->b_blocknr = blocknr; /* set back to the given block address */
 124        *submit_ptr = pblocknr;
 125        err = 0;
 126found:
 127        *pbh = bh;
 128
 129out_locked:
 130        unlock_page(page);
 131        page_cache_release(page);
 132        return err;
 133}
 134
 135/**
 136 * nilfs_btnode_delete - delete B-tree node buffer
 137 * @bh: buffer to be deleted
 138 *
 139 * nilfs_btnode_delete() invalidates the specified buffer and delete the page
 140 * including the buffer if the page gets unbusy.
 141 */
 142void nilfs_btnode_delete(struct buffer_head *bh)
 143{
 144        struct address_space *mapping;
 145        struct page *page = bh->b_page;
 146        pgoff_t index = page_index(page);
 147        int still_dirty;
 148
 149        page_cache_get(page);
 150        lock_page(page);
 151        wait_on_page_writeback(page);
 152
 153        nilfs_forget_buffer(bh);
 154        still_dirty = PageDirty(page);
 155        mapping = page->mapping;
 156        unlock_page(page);
 157        page_cache_release(page);
 158
 159        if (!still_dirty && mapping)
 160                invalidate_inode_pages2_range(mapping, index, index);
 161}
 162
 163/**
 164 * nilfs_btnode_prepare_change_key
 165 *  prepare to move contents of the block for old key to one of new key.
 166 *  the old buffer will not be removed, but might be reused for new buffer.
 167 *  it might return -ENOMEM because of memory allocation errors,
 168 *  and might return -EIO because of disk read errors.
 169 */
 170int nilfs_btnode_prepare_change_key(struct address_space *btnc,
 171                                    struct nilfs_btnode_chkey_ctxt *ctxt)
 172{
 173        struct buffer_head *obh, *nbh;
 174        struct inode *inode = NILFS_BTNC_I(btnc);
 175        __u64 oldkey = ctxt->oldkey, newkey = ctxt->newkey;
 176        int err;
 177
 178        if (oldkey == newkey)
 179                return 0;
 180
 181        obh = ctxt->bh;
 182        ctxt->newbh = NULL;
 183
 184        if (inode->i_blkbits == PAGE_CACHE_SHIFT) {
 185                lock_page(obh->b_page);
 186                /*
 187                 * We cannot call radix_tree_preload for the kernels older
 188                 * than 2.6.23, because it is not exported for modules.
 189                 */
 190retry:
 191                err = radix_tree_preload(GFP_NOFS & ~__GFP_HIGHMEM);
 192                if (err)
 193                        goto failed_unlock;
 194                /* BUG_ON(oldkey != obh->b_page->index); */
 195                if (unlikely(oldkey != obh->b_page->index))
 196                        NILFS_PAGE_BUG(obh->b_page,
 197                                       "invalid oldkey %lld (newkey=%lld)",
 198                                       (unsigned long long)oldkey,
 199                                       (unsigned long long)newkey);
 200
 201                spin_lock_irq(&btnc->tree_lock);
 202                err = radix_tree_insert(&btnc->page_tree, newkey, obh->b_page);
 203                spin_unlock_irq(&btnc->tree_lock);
 204                /*
 205                 * Note: page->index will not change to newkey until
 206                 * nilfs_btnode_commit_change_key() will be called.
 207                 * To protect the page in intermediate state, the page lock
 208                 * is held.
 209                 */
 210                radix_tree_preload_end();
 211                if (!err)
 212                        return 0;
 213                else if (err != -EEXIST)
 214                        goto failed_unlock;
 215
 216                err = invalidate_inode_pages2_range(btnc, newkey, newkey);
 217                if (!err)
 218                        goto retry;
 219                /* fallback to copy mode */
 220                unlock_page(obh->b_page);
 221        }
 222
 223        nbh = nilfs_btnode_create_block(btnc, newkey);
 224        if (!nbh)
 225                return -ENOMEM;
 226
 227        BUG_ON(nbh == obh);
 228        ctxt->newbh = nbh;
 229        return 0;
 230
 231 failed_unlock:
 232        unlock_page(obh->b_page);
 233        return err;
 234}
 235
 236/**
 237 * nilfs_btnode_commit_change_key
 238 *  commit the change_key operation prepared by prepare_change_key().
 239 */
 240void nilfs_btnode_commit_change_key(struct address_space *btnc,
 241                                    struct nilfs_btnode_chkey_ctxt *ctxt)
 242{
 243        struct buffer_head *obh = ctxt->bh, *nbh = ctxt->newbh;
 244        __u64 oldkey = ctxt->oldkey, newkey = ctxt->newkey;
 245        struct page *opage;
 246
 247        if (oldkey == newkey)
 248                return;
 249
 250        if (nbh == NULL) {      /* blocksize == pagesize */
 251                opage = obh->b_page;
 252                if (unlikely(oldkey != opage->index))
 253                        NILFS_PAGE_BUG(opage,
 254                                       "invalid oldkey %lld (newkey=%lld)",
 255                                       (unsigned long long)oldkey,
 256                                       (unsigned long long)newkey);
 257                mark_buffer_dirty(obh);
 258
 259                spin_lock_irq(&btnc->tree_lock);
 260                radix_tree_delete(&btnc->page_tree, oldkey);
 261                radix_tree_tag_set(&btnc->page_tree, newkey,
 262                                   PAGECACHE_TAG_DIRTY);
 263                spin_unlock_irq(&btnc->tree_lock);
 264
 265                opage->index = obh->b_blocknr = newkey;
 266                unlock_page(opage);
 267        } else {
 268                nilfs_copy_buffer(nbh, obh);
 269                mark_buffer_dirty(nbh);
 270
 271                nbh->b_blocknr = newkey;
 272                ctxt->bh = nbh;
 273                nilfs_btnode_delete(obh); /* will decrement bh->b_count */
 274        }
 275}
 276
 277/**
 278 * nilfs_btnode_abort_change_key
 279 *  abort the change_key operation prepared by prepare_change_key().
 280 */
 281void nilfs_btnode_abort_change_key(struct address_space *btnc,
 282                                   struct nilfs_btnode_chkey_ctxt *ctxt)
 283{
 284        struct buffer_head *nbh = ctxt->newbh;
 285        __u64 oldkey = ctxt->oldkey, newkey = ctxt->newkey;
 286
 287        if (oldkey == newkey)
 288                return;
 289
 290        if (nbh == NULL) {      /* blocksize == pagesize */
 291                spin_lock_irq(&btnc->tree_lock);
 292                radix_tree_delete(&btnc->page_tree, newkey);
 293                spin_unlock_irq(&btnc->tree_lock);
 294                unlock_page(ctxt->bh->b_page);
 295        } else
 296                brelse(nbh);
 297}
 298
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.