diff options
Diffstat (limited to 'ANDROID_3.4.5/fs/nilfs2')
40 files changed, 0 insertions, 20230 deletions
diff --git a/ANDROID_3.4.5/fs/nilfs2/Kconfig b/ANDROID_3.4.5/fs/nilfs2/Kconfig deleted file mode 100644 index 251da07b..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/Kconfig +++ /dev/null @@ -1,25 +0,0 @@ -config NILFS2_FS - tristate "NILFS2 file system support (EXPERIMENTAL)" - depends on EXPERIMENTAL - select CRC32 - help - NILFS2 is a log-structured file system (LFS) supporting continuous - snapshotting. In addition to versioning capability of the entire - file system, users can even restore files mistakenly overwritten or - destroyed just a few seconds ago. Since this file system can keep - consistency like conventional LFS, it achieves quick recovery after - system crashes. - - NILFS2 creates a number of checkpoints every few seconds or per - synchronous write basis (unless there is no change). Users can - select significant versions among continuously created checkpoints, - and can change them into snapshots which will be preserved for long - periods until they are changed back to checkpoints. Each - snapshot is mountable as a read-only file system concurrently with - its writable mount, and this feature is convenient for online backup. - - Some features including atime, extended attributes, and POSIX ACLs, - are not supported yet. - - To compile this file system support as a module, choose M here: the - module will be called nilfs2. If unsure, say N. diff --git a/ANDROID_3.4.5/fs/nilfs2/Makefile b/ANDROID_3.4.5/fs/nilfs2/Makefile deleted file mode 100644 index 85c98737..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -obj-$(CONFIG_NILFS2_FS) += nilfs2.o -nilfs2-y := inode.o file.o dir.o super.o namei.o page.o mdt.o \ - btnode.o bmap.o btree.o direct.o dat.o recovery.o \ - the_nilfs.o segbuf.o segment.o cpfile.o sufile.o \ - ifile.o alloc.o gcinode.o ioctl.o diff --git a/ANDROID_3.4.5/fs/nilfs2/alloc.c b/ANDROID_3.4.5/fs/nilfs2/alloc.c deleted file mode 100644 index eed4d7b2..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/alloc.c +++ /dev/null @@ -1,721 +0,0 @@ -/* - * alloc.c - NILFS dat/inode allocator - * - * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Original code was written by Koji Sato <koji@osrg.net>. - * Two allocators were unified by Ryusuke Konishi <ryusuke@osrg.net>, - * Amagai Yoshiji <amagai@osrg.net>. - */ - -#include <linux/types.h> -#include <linux/buffer_head.h> -#include <linux/fs.h> -#include <linux/bitops.h> -#include <linux/slab.h> -#include "mdt.h" -#include "alloc.h" - - -/** - * nilfs_palloc_groups_per_desc_block - get the number of groups that a group - * descriptor block can maintain - * @inode: inode of metadata file using this allocator - */ -static inline unsigned long -nilfs_palloc_groups_per_desc_block(const struct inode *inode) -{ - return (1UL << inode->i_blkbits) / - sizeof(struct nilfs_palloc_group_desc); -} - -/** - * nilfs_palloc_groups_count - get maximum number of groups - * @inode: inode of metadata file using this allocator - */ -static inline unsigned long -nilfs_palloc_groups_count(const struct inode *inode) -{ - return 1UL << (BITS_PER_LONG - (inode->i_blkbits + 3 /* log2(8) */)); -} - -/** - * nilfs_palloc_init_blockgroup - initialize private variables for allocator - * @inode: inode of metadata file using this allocator - * @entry_size: size of the persistent object - */ -int nilfs_palloc_init_blockgroup(struct inode *inode, unsigned entry_size) -{ - struct nilfs_mdt_info *mi = NILFS_MDT(inode); - - mi->mi_bgl = kmalloc(sizeof(*mi->mi_bgl), GFP_NOFS); - if (!mi->mi_bgl) - return -ENOMEM; - - bgl_lock_init(mi->mi_bgl); - - nilfs_mdt_set_entry_size(inode, entry_size, 0); - - mi->mi_blocks_per_group = - DIV_ROUND_UP(nilfs_palloc_entries_per_group(inode), - mi->mi_entries_per_block) + 1; - /* Number of blocks in a group including entry blocks and - a bitmap block */ - mi->mi_blocks_per_desc_block = - nilfs_palloc_groups_per_desc_block(inode) * - mi->mi_blocks_per_group + 1; - /* Number of blocks per descriptor including the - descriptor block */ - return 0; -} - -/** - * nilfs_palloc_group - get group number and offset from an entry number - * @inode: inode of metadata file using this allocator - * @nr: serial number of the entry (e.g. inode number) - * @offset: pointer to store offset number in the group - */ -static unsigned long nilfs_palloc_group(const struct inode *inode, __u64 nr, - unsigned long *offset) -{ - __u64 group = nr; - - *offset = do_div(group, nilfs_palloc_entries_per_group(inode)); - return group; -} - -/** - * nilfs_palloc_desc_blkoff - get block offset of a group descriptor block - * @inode: inode of metadata file using this allocator - * @group: group number - * - * nilfs_palloc_desc_blkoff() returns block offset of the descriptor - * block which contains a descriptor of the specified group. - */ -static unsigned long -nilfs_palloc_desc_blkoff(const struct inode *inode, unsigned long group) -{ - unsigned long desc_block = - group / nilfs_palloc_groups_per_desc_block(inode); - return desc_block * NILFS_MDT(inode)->mi_blocks_per_desc_block; -} - -/** - * nilfs_palloc_bitmap_blkoff - get block offset of a bitmap block - * @inode: inode of metadata file using this allocator - * @group: group number - * - * nilfs_palloc_bitmap_blkoff() returns block offset of the bitmap - * block used to allocate/deallocate entries in the specified group. - */ -static unsigned long -nilfs_palloc_bitmap_blkoff(const struct inode *inode, unsigned long group) -{ - unsigned long desc_offset = - group % nilfs_palloc_groups_per_desc_block(inode); - return nilfs_palloc_desc_blkoff(inode, group) + 1 + - desc_offset * NILFS_MDT(inode)->mi_blocks_per_group; -} - -/** - * nilfs_palloc_group_desc_nfrees - get the number of free entries in a group - * @inode: inode of metadata file using this allocator - * @group: group number - * @desc: pointer to descriptor structure for the group - */ -static unsigned long -nilfs_palloc_group_desc_nfrees(struct inode *inode, unsigned long group, - const struct nilfs_palloc_group_desc *desc) -{ - unsigned long nfree; - - spin_lock(nilfs_mdt_bgl_lock(inode, group)); - nfree = le32_to_cpu(desc->pg_nfrees); - spin_unlock(nilfs_mdt_bgl_lock(inode, group)); - return nfree; -} - -/** - * nilfs_palloc_group_desc_add_entries - adjust count of free entries - * @inode: inode of metadata file using this allocator - * @group: group number - * @desc: pointer to descriptor structure for the group - * @n: delta to be added - */ -static void -nilfs_palloc_group_desc_add_entries(struct inode *inode, - unsigned long group, - struct nilfs_palloc_group_desc *desc, - u32 n) -{ - spin_lock(nilfs_mdt_bgl_lock(inode, group)); - le32_add_cpu(&desc->pg_nfrees, n); - spin_unlock(nilfs_mdt_bgl_lock(inode, group)); -} - -/** - * nilfs_palloc_entry_blkoff - get block offset of an entry block - * @inode: inode of metadata file using this allocator - * @nr: serial number of the entry (e.g. inode number) - */ -static unsigned long -nilfs_palloc_entry_blkoff(const struct inode *inode, __u64 nr) -{ - unsigned long group, group_offset; - - group = nilfs_palloc_group(inode, nr, &group_offset); - - return nilfs_palloc_bitmap_blkoff(inode, group) + 1 + - group_offset / NILFS_MDT(inode)->mi_entries_per_block; -} - -/** - * nilfs_palloc_desc_block_init - initialize buffer of a group descriptor block - * @inode: inode of metadata file - * @bh: buffer head of the buffer to be initialized - * @kaddr: kernel address mapped for the page including the buffer - */ -static void nilfs_palloc_desc_block_init(struct inode *inode, - struct buffer_head *bh, void *kaddr) -{ - struct nilfs_palloc_group_desc *desc = kaddr + bh_offset(bh); - unsigned long n = nilfs_palloc_groups_per_desc_block(inode); - __le32 nfrees; - - nfrees = cpu_to_le32(nilfs_palloc_entries_per_group(inode)); - while (n-- > 0) { - desc->pg_nfrees = nfrees; - desc++; - } -} - -static int nilfs_palloc_get_block(struct inode *inode, unsigned long blkoff, - int create, - void (*init_block)(struct inode *, - struct buffer_head *, - void *), - struct buffer_head **bhp, - struct nilfs_bh_assoc *prev, - spinlock_t *lock) -{ - int ret; - - spin_lock(lock); - if (prev->bh && blkoff == prev->blkoff) { - get_bh(prev->bh); - *bhp = prev->bh; - spin_unlock(lock); - return 0; - } - spin_unlock(lock); - - ret = nilfs_mdt_get_block(inode, blkoff, create, init_block, bhp); - if (!ret) { - spin_lock(lock); - /* - * The following code must be safe for change of the - * cache contents during the get block call. - */ - brelse(prev->bh); - get_bh(*bhp); - prev->bh = *bhp; - prev->blkoff = blkoff; - spin_unlock(lock); - } - return ret; -} - -/** - * nilfs_palloc_get_desc_block - get buffer head of a group descriptor block - * @inode: inode of metadata file using this allocator - * @group: group number - * @create: create flag - * @bhp: pointer to store the resultant buffer head - */ -static int nilfs_palloc_get_desc_block(struct inode *inode, - unsigned long group, - int create, struct buffer_head **bhp) -{ - struct nilfs_palloc_cache *cache = NILFS_MDT(inode)->mi_palloc_cache; - - return nilfs_palloc_get_block(inode, - nilfs_palloc_desc_blkoff(inode, group), - create, nilfs_palloc_desc_block_init, - bhp, &cache->prev_desc, &cache->lock); -} - -/** - * nilfs_palloc_get_bitmap_block - get buffer head of a bitmap block - * @inode: inode of metadata file using this allocator - * @group: group number - * @create: create flag - * @bhp: pointer to store the resultant buffer head - */ -static int nilfs_palloc_get_bitmap_block(struct inode *inode, - unsigned long group, - int create, struct buffer_head **bhp) -{ - struct nilfs_palloc_cache *cache = NILFS_MDT(inode)->mi_palloc_cache; - - return nilfs_palloc_get_block(inode, - nilfs_palloc_bitmap_blkoff(inode, group), - create, NULL, bhp, - &cache->prev_bitmap, &cache->lock); -} - -/** - * nilfs_palloc_get_entry_block - get buffer head of an entry block - * @inode: inode of metadata file using this allocator - * @nr: serial number of the entry (e.g. inode number) - * @create: create flag - * @bhp: pointer to store the resultant buffer head - */ -int nilfs_palloc_get_entry_block(struct inode *inode, __u64 nr, - int create, struct buffer_head **bhp) -{ - struct nilfs_palloc_cache *cache = NILFS_MDT(inode)->mi_palloc_cache; - - return nilfs_palloc_get_block(inode, - nilfs_palloc_entry_blkoff(inode, nr), - create, NULL, bhp, - &cache->prev_entry, &cache->lock); -} - -/** - * nilfs_palloc_block_get_group_desc - get kernel address of a group descriptor - * @inode: inode of metadata file using this allocator - * @group: group number - * @bh: buffer head of the buffer storing the group descriptor block - * @kaddr: kernel address mapped for the page including the buffer - */ -static struct nilfs_palloc_group_desc * -nilfs_palloc_block_get_group_desc(const struct inode *inode, - unsigned long group, - const struct buffer_head *bh, void *kaddr) -{ - return (struct nilfs_palloc_group_desc *)(kaddr + bh_offset(bh)) + - group % nilfs_palloc_groups_per_desc_block(inode); -} - -/** - * nilfs_palloc_block_get_entry - get kernel address of an entry - * @inode: inode of metadata file using this allocator - * @nr: serial number of the entry (e.g. inode number) - * @bh: buffer head of the buffer storing the entry block - * @kaddr: kernel address mapped for the page including the buffer - */ -void *nilfs_palloc_block_get_entry(const struct inode *inode, __u64 nr, - const struct buffer_head *bh, void *kaddr) -{ - unsigned long entry_offset, group_offset; - - nilfs_palloc_group(inode, nr, &group_offset); - entry_offset = group_offset % NILFS_MDT(inode)->mi_entries_per_block; - - return kaddr + bh_offset(bh) + - entry_offset * NILFS_MDT(inode)->mi_entry_size; -} - -/** - * nilfs_palloc_find_available_slot - find available slot in a group - * @inode: inode of metadata file using this allocator - * @group: group number - * @target: offset number of an entry in the group (start point) - * @bitmap: bitmap of the group - * @bsize: size in bits - */ -static int nilfs_palloc_find_available_slot(struct inode *inode, - unsigned long group, - unsigned long target, - unsigned char *bitmap, - int bsize) -{ - int curr, pos, end, i; - - if (target > 0) { - end = (target + BITS_PER_LONG - 1) & ~(BITS_PER_LONG - 1); - if (end > bsize) - end = bsize; - pos = nilfs_find_next_zero_bit(bitmap, end, target); - if (pos < end && - !nilfs_set_bit_atomic( - nilfs_mdt_bgl_lock(inode, group), pos, bitmap)) - return pos; - } else - end = 0; - - for (i = 0, curr = end; - i < bsize; - i += BITS_PER_LONG, curr += BITS_PER_LONG) { - /* wrap around */ - if (curr >= bsize) - curr = 0; - while (*((unsigned long *)bitmap + curr / BITS_PER_LONG) - != ~0UL) { - end = curr + BITS_PER_LONG; - if (end > bsize) - end = bsize; - pos = nilfs_find_next_zero_bit(bitmap, end, curr); - if ((pos < end) && - !nilfs_set_bit_atomic( - nilfs_mdt_bgl_lock(inode, group), pos, - bitmap)) - return pos; - } - } - return -ENOSPC; -} - -/** - * nilfs_palloc_rest_groups_in_desc_block - get the remaining number of groups - * in a group descriptor block - * @inode: inode of metadata file using this allocator - * @curr: current group number - * @max: maximum number of groups - */ -static unsigned long -nilfs_palloc_rest_groups_in_desc_block(const struct inode *inode, - unsigned long curr, unsigned long max) -{ - return min_t(unsigned long, - nilfs_palloc_groups_per_desc_block(inode) - - curr % nilfs_palloc_groups_per_desc_block(inode), - max - curr + 1); -} - -/** - * nilfs_palloc_prepare_alloc_entry - prepare to allocate a persistent object - * @inode: inode of metadata file using this allocator - * @req: nilfs_palloc_req structure exchanged for the allocation - */ -int nilfs_palloc_prepare_alloc_entry(struct inode *inode, - struct nilfs_palloc_req *req) -{ - struct buffer_head *desc_bh, *bitmap_bh; - struct nilfs_palloc_group_desc *desc; - unsigned char *bitmap; - void *desc_kaddr, *bitmap_kaddr; - unsigned long group, maxgroup, ngroups; - unsigned long group_offset, maxgroup_offset; - unsigned long n, entries_per_group, groups_per_desc_block; - unsigned long i, j; - int pos, ret; - - ngroups = nilfs_palloc_groups_count(inode); - maxgroup = ngroups - 1; - group = nilfs_palloc_group(inode, req->pr_entry_nr, &group_offset); - entries_per_group = nilfs_palloc_entries_per_group(inode); - groups_per_desc_block = nilfs_palloc_groups_per_desc_block(inode); - - for (i = 0; i < ngroups; i += n) { - if (group >= ngroups) { - /* wrap around */ - group = 0; - maxgroup = nilfs_palloc_group(inode, req->pr_entry_nr, - &maxgroup_offset) - 1; - } - ret = nilfs_palloc_get_desc_block(inode, group, 1, &desc_bh); - if (ret < 0) - return ret; - desc_kaddr = kmap(desc_bh->b_page); - desc = nilfs_palloc_block_get_group_desc( - inode, group, desc_bh, desc_kaddr); - n = nilfs_palloc_rest_groups_in_desc_block(inode, group, - maxgroup); - for (j = 0; j < n; j++, desc++, group++) { - if (nilfs_palloc_group_desc_nfrees(inode, group, desc) - > 0) { - ret = nilfs_palloc_get_bitmap_block( - inode, group, 1, &bitmap_bh); - if (ret < 0) - goto out_desc; - bitmap_kaddr = kmap(bitmap_bh->b_page); - bitmap = bitmap_kaddr + bh_offset(bitmap_bh); - pos = nilfs_palloc_find_available_slot( - inode, group, group_offset, bitmap, - entries_per_group); - if (pos >= 0) { - /* found a free entry */ - nilfs_palloc_group_desc_add_entries( - inode, group, desc, -1); - req->pr_entry_nr = - entries_per_group * group + pos; - kunmap(desc_bh->b_page); - kunmap(bitmap_bh->b_page); - - req->pr_desc_bh = desc_bh; - req->pr_bitmap_bh = bitmap_bh; - return 0; - } - kunmap(bitmap_bh->b_page); - brelse(bitmap_bh); - } - - group_offset = 0; - } - - kunmap(desc_bh->b_page); - brelse(desc_bh); - } - - /* no entries left */ - return -ENOSPC; - - out_desc: - kunmap(desc_bh->b_page); - brelse(desc_bh); - return ret; -} - -/** - * nilfs_palloc_commit_alloc_entry - finish allocation of a persistent object - * @inode: inode of metadata file using this allocator - * @req: nilfs_palloc_req structure exchanged for the allocation - */ -void nilfs_palloc_commit_alloc_entry(struct inode *inode, - struct nilfs_palloc_req *req) -{ - mark_buffer_dirty(req->pr_bitmap_bh); - mark_buffer_dirty(req->pr_desc_bh); - nilfs_mdt_mark_dirty(inode); - - brelse(req->pr_bitmap_bh); - brelse(req->pr_desc_bh); -} - -/** - * nilfs_palloc_commit_free_entry - finish deallocating a persistent object - * @inode: inode of metadata file using this allocator - * @req: nilfs_palloc_req structure exchanged for the removal - */ -void nilfs_palloc_commit_free_entry(struct inode *inode, - struct nilfs_palloc_req *req) -{ - struct nilfs_palloc_group_desc *desc; - unsigned long group, group_offset; - unsigned char *bitmap; - void *desc_kaddr, *bitmap_kaddr; - - group = nilfs_palloc_group(inode, req->pr_entry_nr, &group_offset); - desc_kaddr = kmap(req->pr_desc_bh->b_page); - desc = nilfs_palloc_block_get_group_desc(inode, group, - req->pr_desc_bh, desc_kaddr); - bitmap_kaddr = kmap(req->pr_bitmap_bh->b_page); - bitmap = bitmap_kaddr + bh_offset(req->pr_bitmap_bh); - - if (!nilfs_clear_bit_atomic(nilfs_mdt_bgl_lock(inode, group), - group_offset, bitmap)) - printk(KERN_WARNING "%s: entry number %llu already freed\n", - __func__, (unsigned long long)req->pr_entry_nr); - else - nilfs_palloc_group_desc_add_entries(inode, group, desc, 1); - - kunmap(req->pr_bitmap_bh->b_page); - kunmap(req->pr_desc_bh->b_page); - - mark_buffer_dirty(req->pr_desc_bh); - mark_buffer_dirty(req->pr_bitmap_bh); - nilfs_mdt_mark_dirty(inode); - - brelse(req->pr_bitmap_bh); - brelse(req->pr_desc_bh); -} - -/** - * nilfs_palloc_abort_alloc_entry - cancel allocation of a persistent object - * @inode: inode of metadata file using this allocator - * @req: nilfs_palloc_req structure exchanged for the allocation - */ -void nilfs_palloc_abort_alloc_entry(struct inode *inode, - struct nilfs_palloc_req *req) -{ - struct nilfs_palloc_group_desc *desc; - void *desc_kaddr, *bitmap_kaddr; - unsigned char *bitmap; - unsigned long group, group_offset; - - group = nilfs_palloc_group(inode, req->pr_entry_nr, &group_offset); - desc_kaddr = kmap(req->pr_desc_bh->b_page); - desc = nilfs_palloc_block_get_group_desc(inode, group, - req->pr_desc_bh, desc_kaddr); - bitmap_kaddr = kmap(req->pr_bitmap_bh->b_page); - bitmap = bitmap_kaddr + bh_offset(req->pr_bitmap_bh); - if (!nilfs_clear_bit_atomic(nilfs_mdt_bgl_lock(inode, group), - group_offset, bitmap)) - printk(KERN_WARNING "%s: entry number %llu already freed\n", - __func__, (unsigned long long)req->pr_entry_nr); - else - nilfs_palloc_group_desc_add_entries(inode, group, desc, 1); - - kunmap(req->pr_bitmap_bh->b_page); - kunmap(req->pr_desc_bh->b_page); - - brelse(req->pr_bitmap_bh); - brelse(req->pr_desc_bh); - - req->pr_entry_nr = 0; - req->pr_bitmap_bh = NULL; - req->pr_desc_bh = NULL; -} - -/** - * nilfs_palloc_prepare_free_entry - prepare to deallocate a persistent object - * @inode: inode of metadata file using this allocator - * @req: nilfs_palloc_req structure exchanged for the removal - */ -int nilfs_palloc_prepare_free_entry(struct inode *inode, - struct nilfs_palloc_req *req) -{ - struct buffer_head *desc_bh, *bitmap_bh; - unsigned long group, group_offset; - int ret; - - group = nilfs_palloc_group(inode, req->pr_entry_nr, &group_offset); - ret = nilfs_palloc_get_desc_block(inode, group, 1, &desc_bh); - if (ret < 0) - return ret; - ret = nilfs_palloc_get_bitmap_block(inode, group, 1, &bitmap_bh); - if (ret < 0) { - brelse(desc_bh); - return ret; - } - - req->pr_desc_bh = desc_bh; - req->pr_bitmap_bh = bitmap_bh; - return 0; -} - -/** - * nilfs_palloc_abort_free_entry - cancel deallocating a persistent object - * @inode: inode of metadata file using this allocator - * @req: nilfs_palloc_req structure exchanged for the removal - */ -void nilfs_palloc_abort_free_entry(struct inode *inode, - struct nilfs_palloc_req *req) -{ - brelse(req->pr_bitmap_bh); - brelse(req->pr_desc_bh); - - req->pr_entry_nr = 0; - req->pr_bitmap_bh = NULL; - req->pr_desc_bh = NULL; -} - -/** - * nilfs_palloc_group_is_in - judge if an entry is in a group - * @inode: inode of metadata file using this allocator - * @group: group number - * @nr: serial number of the entry (e.g. inode number) - */ -static int -nilfs_palloc_group_is_in(struct inode *inode, unsigned long group, __u64 nr) -{ - __u64 first, last; - - first = group * nilfs_palloc_entries_per_group(inode); - last = first + nilfs_palloc_entries_per_group(inode) - 1; - return (nr >= first) && (nr <= last); -} - -/** - * nilfs_palloc_freev - deallocate a set of persistent objects - * @inode: inode of metadata file using this allocator - * @entry_nrs: array of entry numbers to be deallocated - * @nitems: number of entries stored in @entry_nrs - */ -int nilfs_palloc_freev(struct inode *inode, __u64 *entry_nrs, size_t nitems) -{ - struct buffer_head *desc_bh, *bitmap_bh; - struct nilfs_palloc_group_desc *desc; - unsigned char *bitmap; - void *desc_kaddr, *bitmap_kaddr; - unsigned long group, group_offset; - int i, j, n, ret; - - for (i = 0; i < nitems; i = j) { - group = nilfs_palloc_group(inode, entry_nrs[i], &group_offset); - ret = nilfs_palloc_get_desc_block(inode, group, 0, &desc_bh); - if (ret < 0) - return ret; - ret = nilfs_palloc_get_bitmap_block(inode, group, 0, - &bitmap_bh); - if (ret < 0) { - brelse(desc_bh); - return ret; - } - desc_kaddr = kmap(desc_bh->b_page); - desc = nilfs_palloc_block_get_group_desc( - inode, group, desc_bh, desc_kaddr); - bitmap_kaddr = kmap(bitmap_bh->b_page); - bitmap = bitmap_kaddr + bh_offset(bitmap_bh); - for (j = i, n = 0; - (j < nitems) && nilfs_palloc_group_is_in(inode, group, - entry_nrs[j]); - j++) { - nilfs_palloc_group(inode, entry_nrs[j], &group_offset); - if (!nilfs_clear_bit_atomic( - nilfs_mdt_bgl_lock(inode, group), - group_offset, bitmap)) { - printk(KERN_WARNING - "%s: entry number %llu already freed\n", - __func__, - (unsigned long long)entry_nrs[j]); - } else { - n++; - } - } - nilfs_palloc_group_desc_add_entries(inode, group, desc, n); - - kunmap(bitmap_bh->b_page); - kunmap(desc_bh->b_page); - - mark_buffer_dirty(desc_bh); - mark_buffer_dirty(bitmap_bh); - nilfs_mdt_mark_dirty(inode); - - brelse(bitmap_bh); - brelse(desc_bh); - } - return 0; -} - -void nilfs_palloc_setup_cache(struct inode *inode, - struct nilfs_palloc_cache *cache) -{ - NILFS_MDT(inode)->mi_palloc_cache = cache; - spin_lock_init(&cache->lock); -} - -void nilfs_palloc_clear_cache(struct inode *inode) -{ - struct nilfs_palloc_cache *cache = NILFS_MDT(inode)->mi_palloc_cache; - - spin_lock(&cache->lock); - brelse(cache->prev_desc.bh); - brelse(cache->prev_bitmap.bh); - brelse(cache->prev_entry.bh); - cache->prev_desc.bh = NULL; - cache->prev_bitmap.bh = NULL; - cache->prev_entry.bh = NULL; - spin_unlock(&cache->lock); -} - -void nilfs_palloc_destroy_cache(struct inode *inode) -{ - nilfs_palloc_clear_cache(inode); - NILFS_MDT(inode)->mi_palloc_cache = NULL; -} diff --git a/ANDROID_3.4.5/fs/nilfs2/alloc.h b/ANDROID_3.4.5/fs/nilfs2/alloc.h deleted file mode 100644 index f5fde36b..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/alloc.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * alloc.h - persistent object (dat entry/disk inode) allocator/deallocator - * - * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Original code was written by Koji Sato <koji@osrg.net>. - * Two allocators were unified by Ryusuke Konishi <ryusuke@osrg.net>, - * Amagai Yoshiji <amagai@osrg.net>. - */ - -#ifndef _NILFS_ALLOC_H -#define _NILFS_ALLOC_H - -#include <linux/types.h> -#include <linux/buffer_head.h> -#include <linux/fs.h> - -/** - * nilfs_palloc_entries_per_group - get the number of entries per group - * @inode: inode of metadata file using this allocator - * - * The number of entries per group is defined by the number of bits - * that a bitmap block can maintain. - */ -static inline unsigned long -nilfs_palloc_entries_per_group(const struct inode *inode) -{ - return 1UL << (inode->i_blkbits + 3 /* log2(8 = CHAR_BITS) */); -} - -int nilfs_palloc_init_blockgroup(struct inode *, unsigned); -int nilfs_palloc_get_entry_block(struct inode *, __u64, int, - struct buffer_head **); -void *nilfs_palloc_block_get_entry(const struct inode *, __u64, - const struct buffer_head *, void *); - -/** - * nilfs_palloc_req - persistent allocator request and reply - * @pr_entry_nr: entry number (vblocknr or inode number) - * @pr_desc_bh: buffer head of the buffer containing block group descriptors - * @pr_bitmap_bh: buffer head of the buffer containing a block group bitmap - * @pr_entry_bh: buffer head of the buffer containing translation entries - */ -struct nilfs_palloc_req { - __u64 pr_entry_nr; - struct buffer_head *pr_desc_bh; - struct buffer_head *pr_bitmap_bh; - struct buffer_head *pr_entry_bh; -}; - -int nilfs_palloc_prepare_alloc_entry(struct inode *, - struct nilfs_palloc_req *); -void nilfs_palloc_commit_alloc_entry(struct inode *, - struct nilfs_palloc_req *); -void nilfs_palloc_abort_alloc_entry(struct inode *, struct nilfs_palloc_req *); -void nilfs_palloc_commit_free_entry(struct inode *, struct nilfs_palloc_req *); -int nilfs_palloc_prepare_free_entry(struct inode *, struct nilfs_palloc_req *); -void nilfs_palloc_abort_free_entry(struct inode *, struct nilfs_palloc_req *); -int nilfs_palloc_freev(struct inode *, __u64 *, size_t); - -#define nilfs_set_bit_atomic ext2_set_bit_atomic -#define nilfs_clear_bit_atomic ext2_clear_bit_atomic -#define nilfs_find_next_zero_bit find_next_zero_bit_le - -/* - * persistent object allocator cache - */ - -struct nilfs_bh_assoc { - unsigned long blkoff; - struct buffer_head *bh; -}; - -struct nilfs_palloc_cache { - spinlock_t lock; - struct nilfs_bh_assoc prev_desc; - struct nilfs_bh_assoc prev_bitmap; - struct nilfs_bh_assoc prev_entry; -}; - -void nilfs_palloc_setup_cache(struct inode *inode, - struct nilfs_palloc_cache *cache); -void nilfs_palloc_clear_cache(struct inode *inode); -void nilfs_palloc_destroy_cache(struct inode *inode); - -#endif /* _NILFS_ALLOC_H */ diff --git a/ANDROID_3.4.5/fs/nilfs2/bmap.c b/ANDROID_3.4.5/fs/nilfs2/bmap.c deleted file mode 100644 index aadbd0b5..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/bmap.c +++ /dev/null @@ -1,567 +0,0 @@ -/* - * bmap.c - NILFS block mapping. - * - * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Koji Sato <koji@osrg.net>. - */ - -#include <linux/fs.h> -#include <linux/string.h> -#include <linux/errno.h> -#include "nilfs.h" -#include "bmap.h" -#include "btree.h" -#include "direct.h" -#include "btnode.h" -#include "mdt.h" -#include "dat.h" -#include "alloc.h" - -struct inode *nilfs_bmap_get_dat(const struct nilfs_bmap *bmap) -{ - struct the_nilfs *nilfs = bmap->b_inode->i_sb->s_fs_info; - - return nilfs->ns_dat; -} - -static int nilfs_bmap_convert_error(struct nilfs_bmap *bmap, - const char *fname, int err) -{ - struct inode *inode = bmap->b_inode; - - if (err == -EINVAL) { - nilfs_error(inode->i_sb, fname, - "broken bmap (inode number=%lu)\n", inode->i_ino); - err = -EIO; - } - return err; -} - -/** - * nilfs_bmap_lookup_at_level - find a data block or node block - * @bmap: bmap - * @key: key - * @level: level - * @ptrp: place to store the value associated to @key - * - * Description: nilfs_bmap_lookup_at_level() finds a record whose key - * matches @key in the block at @level of the bmap. - * - * Return Value: On success, 0 is returned and the record associated with @key - * is stored in the place pointed by @ptrp. On error, one of the following - * negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - * - * %-ENOENT - A record associated with @key does not exist. - */ -int nilfs_bmap_lookup_at_level(struct nilfs_bmap *bmap, __u64 key, int level, - __u64 *ptrp) -{ - sector_t blocknr; - int ret; - - down_read(&bmap->b_sem); - ret = bmap->b_ops->bop_lookup(bmap, key, level, ptrp); - if (ret < 0) { - ret = nilfs_bmap_convert_error(bmap, __func__, ret); - goto out; - } - if (NILFS_BMAP_USE_VBN(bmap)) { - ret = nilfs_dat_translate(nilfs_bmap_get_dat(bmap), *ptrp, - &blocknr); - if (!ret) - *ptrp = blocknr; - } - - out: - up_read(&bmap->b_sem); - return ret; -} - -int nilfs_bmap_lookup_contig(struct nilfs_bmap *bmap, __u64 key, __u64 *ptrp, - unsigned maxblocks) -{ - int ret; - - down_read(&bmap->b_sem); - ret = bmap->b_ops->bop_lookup_contig(bmap, key, ptrp, maxblocks); - up_read(&bmap->b_sem); - - return nilfs_bmap_convert_error(bmap, __func__, ret); -} - -static int nilfs_bmap_do_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr) -{ - __u64 keys[NILFS_BMAP_SMALL_HIGH + 1]; - __u64 ptrs[NILFS_BMAP_SMALL_HIGH + 1]; - int ret, n; - - if (bmap->b_ops->bop_check_insert != NULL) { - ret = bmap->b_ops->bop_check_insert(bmap, key); - if (ret > 0) { - n = bmap->b_ops->bop_gather_data( - bmap, keys, ptrs, NILFS_BMAP_SMALL_HIGH + 1); - if (n < 0) - return n; - ret = nilfs_btree_convert_and_insert( - bmap, key, ptr, keys, ptrs, n); - if (ret == 0) - bmap->b_u.u_flags |= NILFS_BMAP_LARGE; - - return ret; - } else if (ret < 0) - return ret; - } - - return bmap->b_ops->bop_insert(bmap, key, ptr); -} - -/** - * nilfs_bmap_insert - insert a new key-record pair into a bmap - * @bmap: bmap - * @key: key - * @rec: record - * - * Description: nilfs_bmap_insert() inserts the new key-record pair specified - * by @key and @rec into @bmap. - * - * Return Value: On success, 0 is returned. On error, one of the following - * negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - * - * %-EEXIST - A record associated with @key already exist. - */ -int nilfs_bmap_insert(struct nilfs_bmap *bmap, - unsigned long key, - unsigned long rec) -{ - int ret; - - down_write(&bmap->b_sem); - ret = nilfs_bmap_do_insert(bmap, key, rec); - up_write(&bmap->b_sem); - - return nilfs_bmap_convert_error(bmap, __func__, ret); -} - -static int nilfs_bmap_do_delete(struct nilfs_bmap *bmap, __u64 key) -{ - __u64 keys[NILFS_BMAP_LARGE_LOW + 1]; - __u64 ptrs[NILFS_BMAP_LARGE_LOW + 1]; - int ret, n; - - if (bmap->b_ops->bop_check_delete != NULL) { - ret = bmap->b_ops->bop_check_delete(bmap, key); - if (ret > 0) { - n = bmap->b_ops->bop_gather_data( - bmap, keys, ptrs, NILFS_BMAP_LARGE_LOW + 1); - if (n < 0) - return n; - ret = nilfs_direct_delete_and_convert( - bmap, key, keys, ptrs, n); - if (ret == 0) - bmap->b_u.u_flags &= ~NILFS_BMAP_LARGE; - - return ret; - } else if (ret < 0) - return ret; - } - - return bmap->b_ops->bop_delete(bmap, key); -} - -int nilfs_bmap_last_key(struct nilfs_bmap *bmap, unsigned long *key) -{ - __u64 lastkey; - int ret; - - down_read(&bmap->b_sem); - ret = bmap->b_ops->bop_last_key(bmap, &lastkey); - up_read(&bmap->b_sem); - - if (ret < 0) - ret = nilfs_bmap_convert_error(bmap, __func__, ret); - else - *key = lastkey; - return ret; -} - -/** - * nilfs_bmap_delete - delete a key-record pair from a bmap - * @bmap: bmap - * @key: key - * - * Description: nilfs_bmap_delete() deletes the key-record pair specified by - * @key from @bmap. - * - * Return Value: On success, 0 is returned. On error, one of the following - * negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - * - * %-ENOENT - A record associated with @key does not exist. - */ -int nilfs_bmap_delete(struct nilfs_bmap *bmap, unsigned long key) -{ - int ret; - - down_write(&bmap->b_sem); - ret = nilfs_bmap_do_delete(bmap, key); - up_write(&bmap->b_sem); - - return nilfs_bmap_convert_error(bmap, __func__, ret); -} - -static int nilfs_bmap_do_truncate(struct nilfs_bmap *bmap, unsigned long key) -{ - __u64 lastkey; - int ret; - - ret = bmap->b_ops->bop_last_key(bmap, &lastkey); - if (ret < 0) { - if (ret == -ENOENT) - ret = 0; - return ret; - } - - while (key <= lastkey) { - ret = nilfs_bmap_do_delete(bmap, lastkey); - if (ret < 0) - return ret; - ret = bmap->b_ops->bop_last_key(bmap, &lastkey); - if (ret < 0) { - if (ret == -ENOENT) - ret = 0; - return ret; - } - } - return 0; -} - -/** - * nilfs_bmap_truncate - truncate a bmap to a specified key - * @bmap: bmap - * @key: key - * - * Description: nilfs_bmap_truncate() removes key-record pairs whose keys are - * greater than or equal to @key from @bmap. - * - * Return Value: On success, 0 is returned. On error, one of the following - * negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - */ -int nilfs_bmap_truncate(struct nilfs_bmap *bmap, unsigned long key) -{ - int ret; - - down_write(&bmap->b_sem); - ret = nilfs_bmap_do_truncate(bmap, key); - up_write(&bmap->b_sem); - - return nilfs_bmap_convert_error(bmap, __func__, ret); -} - -/** - * nilfs_bmap_clear - free resources a bmap holds - * @bmap: bmap - * - * Description: nilfs_bmap_clear() frees resources associated with @bmap. - */ -void nilfs_bmap_clear(struct nilfs_bmap *bmap) -{ - down_write(&bmap->b_sem); - if (bmap->b_ops->bop_clear != NULL) - bmap->b_ops->bop_clear(bmap); - up_write(&bmap->b_sem); -} - -/** - * nilfs_bmap_propagate - propagate dirty state - * @bmap: bmap - * @bh: buffer head - * - * Description: nilfs_bmap_propagate() marks the buffers that directly or - * indirectly refer to the block specified by @bh dirty. - * - * Return Value: On success, 0 is returned. On error, one of the following - * negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - */ -int nilfs_bmap_propagate(struct nilfs_bmap *bmap, struct buffer_head *bh) -{ - int ret; - - down_write(&bmap->b_sem); - ret = bmap->b_ops->bop_propagate(bmap, bh); - up_write(&bmap->b_sem); - - return nilfs_bmap_convert_error(bmap, __func__, ret); -} - -/** - * nilfs_bmap_lookup_dirty_buffers - - * @bmap: bmap - * @listp: pointer to buffer head list - */ -void nilfs_bmap_lookup_dirty_buffers(struct nilfs_bmap *bmap, - struct list_head *listp) -{ - if (bmap->b_ops->bop_lookup_dirty_buffers != NULL) - bmap->b_ops->bop_lookup_dirty_buffers(bmap, listp); -} - -/** - * nilfs_bmap_assign - assign a new block number to a block - * @bmap: bmap - * @bhp: pointer to buffer head - * @blocknr: block number - * @binfo: block information - * - * Description: nilfs_bmap_assign() assigns the block number @blocknr to the - * buffer specified by @bh. - * - * Return Value: On success, 0 is returned and the buffer head of a newly - * create buffer and the block information associated with the buffer are - * stored in the place pointed by @bh and @binfo, respectively. On error, one - * of the following negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - */ -int nilfs_bmap_assign(struct nilfs_bmap *bmap, - struct buffer_head **bh, - unsigned long blocknr, - union nilfs_binfo *binfo) -{ - int ret; - - down_write(&bmap->b_sem); - ret = bmap->b_ops->bop_assign(bmap, bh, blocknr, binfo); - up_write(&bmap->b_sem); - - return nilfs_bmap_convert_error(bmap, __func__, ret); -} - -/** - * nilfs_bmap_mark - mark block dirty - * @bmap: bmap - * @key: key - * @level: level - * - * Description: nilfs_bmap_mark() marks the block specified by @key and @level - * as dirty. - * - * Return Value: On success, 0 is returned. On error, one of the following - * negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - */ -int nilfs_bmap_mark(struct nilfs_bmap *bmap, __u64 key, int level) -{ - int ret; - - if (bmap->b_ops->bop_mark == NULL) - return 0; - - down_write(&bmap->b_sem); - ret = bmap->b_ops->bop_mark(bmap, key, level); - up_write(&bmap->b_sem); - - return nilfs_bmap_convert_error(bmap, __func__, ret); -} - -/** - * nilfs_bmap_test_and_clear_dirty - test and clear a bmap dirty state - * @bmap: bmap - * - * Description: nilfs_test_and_clear() is the atomic operation to test and - * clear the dirty state of @bmap. - * - * Return Value: 1 is returned if @bmap is dirty, or 0 if clear. - */ -int nilfs_bmap_test_and_clear_dirty(struct nilfs_bmap *bmap) -{ - int ret; - - down_write(&bmap->b_sem); - ret = nilfs_bmap_dirty(bmap); - nilfs_bmap_clear_dirty(bmap); - up_write(&bmap->b_sem); - return ret; -} - - -/* - * Internal use only - */ -__u64 nilfs_bmap_data_get_key(const struct nilfs_bmap *bmap, - const struct buffer_head *bh) -{ - struct buffer_head *pbh; - __u64 key; - - key = page_index(bh->b_page) << (PAGE_CACHE_SHIFT - - bmap->b_inode->i_blkbits); - for (pbh = page_buffers(bh->b_page); pbh != bh; pbh = pbh->b_this_page) - key++; - - return key; -} - -__u64 nilfs_bmap_find_target_seq(const struct nilfs_bmap *bmap, __u64 key) -{ - __s64 diff; - - diff = key - bmap->b_last_allocated_key; - if ((nilfs_bmap_keydiff_abs(diff) < NILFS_INODE_BMAP_SIZE) && - (bmap->b_last_allocated_ptr != NILFS_BMAP_INVALID_PTR) && - (bmap->b_last_allocated_ptr + diff > 0)) - return bmap->b_last_allocated_ptr + diff; - else - return NILFS_BMAP_INVALID_PTR; -} - -#define NILFS_BMAP_GROUP_DIV 8 -__u64 nilfs_bmap_find_target_in_group(const struct nilfs_bmap *bmap) -{ - struct inode *dat = nilfs_bmap_get_dat(bmap); - unsigned long entries_per_group = nilfs_palloc_entries_per_group(dat); - unsigned long group = bmap->b_inode->i_ino / entries_per_group; - - return group * entries_per_group + - (bmap->b_inode->i_ino % NILFS_BMAP_GROUP_DIV) * - (entries_per_group / NILFS_BMAP_GROUP_DIV); -} - -static struct lock_class_key nilfs_bmap_dat_lock_key; -static struct lock_class_key nilfs_bmap_mdt_lock_key; - -/** - * nilfs_bmap_read - read a bmap from an inode - * @bmap: bmap - * @raw_inode: on-disk inode - * - * Description: nilfs_bmap_read() initializes the bmap @bmap. - * - * Return Value: On success, 0 is returned. On error, the following negative - * error code is returned. - * - * %-ENOMEM - Insufficient amount of memory available. - */ -int nilfs_bmap_read(struct nilfs_bmap *bmap, struct nilfs_inode *raw_inode) -{ - if (raw_inode == NULL) - memset(bmap->b_u.u_data, 0, NILFS_BMAP_SIZE); - else - memcpy(bmap->b_u.u_data, raw_inode->i_bmap, NILFS_BMAP_SIZE); - - init_rwsem(&bmap->b_sem); - bmap->b_state = 0; - bmap->b_inode = &NILFS_BMAP_I(bmap)->vfs_inode; - switch (bmap->b_inode->i_ino) { - case NILFS_DAT_INO: - bmap->b_ptr_type = NILFS_BMAP_PTR_P; - bmap->b_last_allocated_key = 0; - bmap->b_last_allocated_ptr = NILFS_BMAP_NEW_PTR_INIT; - lockdep_set_class(&bmap->b_sem, &nilfs_bmap_dat_lock_key); - break; - case NILFS_CPFILE_INO: - case NILFS_SUFILE_INO: - bmap->b_ptr_type = NILFS_BMAP_PTR_VS; - bmap->b_last_allocated_key = 0; - bmap->b_last_allocated_ptr = NILFS_BMAP_INVALID_PTR; - lockdep_set_class(&bmap->b_sem, &nilfs_bmap_mdt_lock_key); - break; - case NILFS_IFILE_INO: - lockdep_set_class(&bmap->b_sem, &nilfs_bmap_mdt_lock_key); - /* Fall through */ - default: - bmap->b_ptr_type = NILFS_BMAP_PTR_VM; - bmap->b_last_allocated_key = 0; - bmap->b_last_allocated_ptr = NILFS_BMAP_INVALID_PTR; - break; - } - - return (bmap->b_u.u_flags & NILFS_BMAP_LARGE) ? - nilfs_btree_init(bmap) : nilfs_direct_init(bmap); -} - -/** - * nilfs_bmap_write - write back a bmap to an inode - * @bmap: bmap - * @raw_inode: on-disk inode - * - * Description: nilfs_bmap_write() stores @bmap in @raw_inode. - */ -void nilfs_bmap_write(struct nilfs_bmap *bmap, struct nilfs_inode *raw_inode) -{ - down_write(&bmap->b_sem); - memcpy(raw_inode->i_bmap, bmap->b_u.u_data, - NILFS_INODE_BMAP_SIZE * sizeof(__le64)); - if (bmap->b_inode->i_ino == NILFS_DAT_INO) - bmap->b_last_allocated_ptr = NILFS_BMAP_NEW_PTR_INIT; - - up_write(&bmap->b_sem); -} - -void nilfs_bmap_init_gc(struct nilfs_bmap *bmap) -{ - memset(&bmap->b_u, 0, NILFS_BMAP_SIZE); - init_rwsem(&bmap->b_sem); - bmap->b_inode = &NILFS_BMAP_I(bmap)->vfs_inode; - bmap->b_ptr_type = NILFS_BMAP_PTR_U; - bmap->b_last_allocated_key = 0; - bmap->b_last_allocated_ptr = NILFS_BMAP_INVALID_PTR; - bmap->b_state = 0; - nilfs_btree_init_gc(bmap); -} - -void nilfs_bmap_save(const struct nilfs_bmap *bmap, - struct nilfs_bmap_store *store) -{ - memcpy(store->data, bmap->b_u.u_data, sizeof(store->data)); - store->last_allocated_key = bmap->b_last_allocated_key; - store->last_allocated_ptr = bmap->b_last_allocated_ptr; - store->state = bmap->b_state; -} - -void nilfs_bmap_restore(struct nilfs_bmap *bmap, - const struct nilfs_bmap_store *store) -{ - memcpy(bmap->b_u.u_data, store->data, sizeof(store->data)); - bmap->b_last_allocated_key = store->last_allocated_key; - bmap->b_last_allocated_ptr = store->last_allocated_ptr; - bmap->b_state = store->state; -} diff --git a/ANDROID_3.4.5/fs/nilfs2/bmap.h b/ANDROID_3.4.5/fs/nilfs2/bmap.h deleted file mode 100644 index 40d9f453..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/bmap.h +++ /dev/null @@ -1,270 +0,0 @@ -/* - * bmap.h - NILFS block mapping. - * - * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Koji Sato <koji@osrg.net>. - */ - -#ifndef _NILFS_BMAP_H -#define _NILFS_BMAP_H - -#include <linux/types.h> -#include <linux/fs.h> -#include <linux/buffer_head.h> -#include <linux/nilfs2_fs.h> -#include "alloc.h" -#include "dat.h" - -#define NILFS_BMAP_INVALID_PTR 0 - -#define nilfs_bmap_keydiff_abs(diff) ((diff) < 0 ? -(diff) : (diff)) - - -struct nilfs_bmap; - -/** - * union nilfs_bmap_ptr_req - request for bmap ptr - * @bpr_ptr: bmap pointer - * @bpr_req: request for persistent allocator - */ -union nilfs_bmap_ptr_req { - __u64 bpr_ptr; - struct nilfs_palloc_req bpr_req; -}; - -/** - * struct nilfs_bmap_stats - bmap statistics - * @bs_nblocks: number of blocks created or deleted - */ -struct nilfs_bmap_stats { - unsigned int bs_nblocks; -}; - -/** - * struct nilfs_bmap_operations - bmap operation table - */ -struct nilfs_bmap_operations { - int (*bop_lookup)(const struct nilfs_bmap *, __u64, int, __u64 *); - int (*bop_lookup_contig)(const struct nilfs_bmap *, __u64, __u64 *, - unsigned); - int (*bop_insert)(struct nilfs_bmap *, __u64, __u64); - int (*bop_delete)(struct nilfs_bmap *, __u64); - void (*bop_clear)(struct nilfs_bmap *); - - int (*bop_propagate)(struct nilfs_bmap *, struct buffer_head *); - void (*bop_lookup_dirty_buffers)(struct nilfs_bmap *, - struct list_head *); - - int (*bop_assign)(struct nilfs_bmap *, - struct buffer_head **, - sector_t, - union nilfs_binfo *); - int (*bop_mark)(struct nilfs_bmap *, __u64, int); - - /* The following functions are internal use only. */ - int (*bop_last_key)(const struct nilfs_bmap *, __u64 *); - int (*bop_check_insert)(const struct nilfs_bmap *, __u64); - int (*bop_check_delete)(struct nilfs_bmap *, __u64); - int (*bop_gather_data)(struct nilfs_bmap *, __u64 *, __u64 *, int); -}; - - -#define NILFS_BMAP_SIZE (NILFS_INODE_BMAP_SIZE * sizeof(__le64)) -#define NILFS_BMAP_KEY_BIT (sizeof(unsigned long) * 8 /* CHAR_BIT */) -#define NILFS_BMAP_NEW_PTR_INIT \ - (1UL << (sizeof(unsigned long) * 8 /* CHAR_BIT */ - 1)) - -static inline int nilfs_bmap_is_new_ptr(unsigned long ptr) -{ - return !!(ptr & NILFS_BMAP_NEW_PTR_INIT); -} - - -/** - * struct nilfs_bmap - bmap structure - * @b_u: raw data - * @b_sem: semaphore - * @b_inode: owner of bmap - * @b_ops: bmap operation table - * @b_last_allocated_key: last allocated key for data block - * @b_last_allocated_ptr: last allocated ptr for data block - * @b_ptr_type: pointer type - * @b_state: state - * @b_nchildren_per_block: maximum number of child nodes for non-root nodes - */ -struct nilfs_bmap { - union { - __u8 u_flags; - __le64 u_data[NILFS_BMAP_SIZE / sizeof(__le64)]; - } b_u; - struct rw_semaphore b_sem; - struct inode *b_inode; - const struct nilfs_bmap_operations *b_ops; - __u64 b_last_allocated_key; - __u64 b_last_allocated_ptr; - int b_ptr_type; - int b_state; - __u16 b_nchildren_per_block; -}; - -/* pointer type */ -#define NILFS_BMAP_PTR_P 0 /* physical block number (i.e. LBN) */ -#define NILFS_BMAP_PTR_VS 1 /* virtual block number (single - version) */ -#define NILFS_BMAP_PTR_VM 2 /* virtual block number (has multiple - versions) */ -#define NILFS_BMAP_PTR_U (-1) /* never perform pointer operations */ - -#define NILFS_BMAP_USE_VBN(bmap) ((bmap)->b_ptr_type > 0) - -/* state */ -#define NILFS_BMAP_DIRTY 0x00000001 - -struct nilfs_bmap_store { - __le64 data[NILFS_BMAP_SIZE / sizeof(__le64)]; - __u64 last_allocated_key; - __u64 last_allocated_ptr; - int state; -}; - -int nilfs_bmap_test_and_clear_dirty(struct nilfs_bmap *); -int nilfs_bmap_read(struct nilfs_bmap *, struct nilfs_inode *); -void nilfs_bmap_write(struct nilfs_bmap *, struct nilfs_inode *); -int nilfs_bmap_lookup_contig(struct nilfs_bmap *, __u64, __u64 *, unsigned); -int nilfs_bmap_insert(struct nilfs_bmap *, unsigned long, unsigned long); -int nilfs_bmap_delete(struct nilfs_bmap *, unsigned long); -int nilfs_bmap_last_key(struct nilfs_bmap *, unsigned long *); -int nilfs_bmap_truncate(struct nilfs_bmap *, unsigned long); -void nilfs_bmap_clear(struct nilfs_bmap *); -int nilfs_bmap_propagate(struct nilfs_bmap *, struct buffer_head *); -void nilfs_bmap_lookup_dirty_buffers(struct nilfs_bmap *, struct list_head *); -int nilfs_bmap_assign(struct nilfs_bmap *, struct buffer_head **, - unsigned long, union nilfs_binfo *); -int nilfs_bmap_lookup_at_level(struct nilfs_bmap *, __u64, int, __u64 *); -int nilfs_bmap_mark(struct nilfs_bmap *, __u64, int); - -void nilfs_bmap_init_gc(struct nilfs_bmap *); - -void nilfs_bmap_save(const struct nilfs_bmap *, struct nilfs_bmap_store *); -void nilfs_bmap_restore(struct nilfs_bmap *, const struct nilfs_bmap_store *); - -static inline int nilfs_bmap_lookup(struct nilfs_bmap *bmap, __u64 key, - __u64 *ptr) -{ - return nilfs_bmap_lookup_at_level(bmap, key, 1, ptr); -} - -/* - * Internal use only - */ -struct inode *nilfs_bmap_get_dat(const struct nilfs_bmap *); - -static inline int nilfs_bmap_prepare_alloc_ptr(struct nilfs_bmap *bmap, - union nilfs_bmap_ptr_req *req, - struct inode *dat) -{ - if (dat) - return nilfs_dat_prepare_alloc(dat, &req->bpr_req); - /* ignore target ptr */ - req->bpr_ptr = bmap->b_last_allocated_ptr++; - return 0; -} - -static inline void nilfs_bmap_commit_alloc_ptr(struct nilfs_bmap *bmap, - union nilfs_bmap_ptr_req *req, - struct inode *dat) -{ - if (dat) - nilfs_dat_commit_alloc(dat, &req->bpr_req); -} - -static inline void nilfs_bmap_abort_alloc_ptr(struct nilfs_bmap *bmap, - union nilfs_bmap_ptr_req *req, - struct inode *dat) -{ - if (dat) - nilfs_dat_abort_alloc(dat, &req->bpr_req); - else - bmap->b_last_allocated_ptr--; -} - -static inline int nilfs_bmap_prepare_end_ptr(struct nilfs_bmap *bmap, - union nilfs_bmap_ptr_req *req, - struct inode *dat) -{ - return dat ? nilfs_dat_prepare_end(dat, &req->bpr_req) : 0; -} - -static inline void nilfs_bmap_commit_end_ptr(struct nilfs_bmap *bmap, - union nilfs_bmap_ptr_req *req, - struct inode *dat) -{ - if (dat) - nilfs_dat_commit_end(dat, &req->bpr_req, - bmap->b_ptr_type == NILFS_BMAP_PTR_VS); -} - -static inline void nilfs_bmap_abort_end_ptr(struct nilfs_bmap *bmap, - union nilfs_bmap_ptr_req *req, - struct inode *dat) -{ - if (dat) - nilfs_dat_abort_end(dat, &req->bpr_req); -} - -static inline void nilfs_bmap_set_target_v(struct nilfs_bmap *bmap, __u64 key, - __u64 ptr) -{ - bmap->b_last_allocated_key = key; - bmap->b_last_allocated_ptr = ptr; -} - -__u64 nilfs_bmap_data_get_key(const struct nilfs_bmap *, - const struct buffer_head *); - -__u64 nilfs_bmap_find_target_seq(const struct nilfs_bmap *, __u64); -__u64 nilfs_bmap_find_target_in_group(const struct nilfs_bmap *); - - -/* Assume that bmap semaphore is locked. */ -static inline int nilfs_bmap_dirty(const struct nilfs_bmap *bmap) -{ - return !!(bmap->b_state & NILFS_BMAP_DIRTY); -} - -/* Assume that bmap semaphore is locked. */ -static inline void nilfs_bmap_set_dirty(struct nilfs_bmap *bmap) -{ - bmap->b_state |= NILFS_BMAP_DIRTY; -} - -/* Assume that bmap semaphore is locked. */ -static inline void nilfs_bmap_clear_dirty(struct nilfs_bmap *bmap) -{ - bmap->b_state &= ~NILFS_BMAP_DIRTY; -} - - -#define NILFS_BMAP_LARGE 0x1 - -#define NILFS_BMAP_SMALL_LOW NILFS_DIRECT_KEY_MIN -#define NILFS_BMAP_SMALL_HIGH NILFS_DIRECT_KEY_MAX -#define NILFS_BMAP_LARGE_LOW NILFS_BTREE_ROOT_NCHILDREN_MAX -#define NILFS_BMAP_LARGE_HIGH NILFS_BTREE_KEY_MAX - -#endif /* _NILFS_BMAP_H */ diff --git a/ANDROID_3.4.5/fs/nilfs2/btnode.c b/ANDROID_3.4.5/fs/nilfs2/btnode.c deleted file mode 100644 index a35ae35e..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/btnode.c +++ /dev/null @@ -1,297 +0,0 @@ -/* - * btnode.c - NILFS B-tree node cache - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * This file was originally written by Seiji Kihara <kihara@osrg.net> - * and fully revised by Ryusuke Konishi <ryusuke@osrg.net> for - * stabilization and simplification. - * - */ - -#include <linux/types.h> -#include <linux/buffer_head.h> -#include <linux/mm.h> -#include <linux/backing-dev.h> -#include <linux/gfp.h> -#include "nilfs.h" -#include "mdt.h" -#include "dat.h" -#include "page.h" -#include "btnode.h" - -void nilfs_btnode_cache_clear(struct address_space *btnc) -{ - invalidate_mapping_pages(btnc, 0, -1); - truncate_inode_pages(btnc, 0); -} - -struct buffer_head * -nilfs_btnode_create_block(struct address_space *btnc, __u64 blocknr) -{ - struct inode *inode = NILFS_BTNC_I(btnc); - struct buffer_head *bh; - - bh = nilfs_grab_buffer(inode, btnc, blocknr, 1 << BH_NILFS_Node); - if (unlikely(!bh)) - return NULL; - - if (unlikely(buffer_mapped(bh) || buffer_uptodate(bh) || - buffer_dirty(bh))) { - brelse(bh); - BUG(); - } - memset(bh->b_data, 0, 1 << inode->i_blkbits); - bh->b_bdev = inode->i_sb->s_bdev; - bh->b_blocknr = blocknr; - set_buffer_mapped(bh); - set_buffer_uptodate(bh); - - unlock_page(bh->b_page); - page_cache_release(bh->b_page); - return bh; -} - -int nilfs_btnode_submit_block(struct address_space *btnc, __u64 blocknr, - sector_t pblocknr, int mode, - struct buffer_head **pbh, sector_t *submit_ptr) -{ - struct buffer_head *bh; - struct inode *inode = NILFS_BTNC_I(btnc); - struct page *page; - int err; - - bh = nilfs_grab_buffer(inode, btnc, blocknr, 1 << BH_NILFS_Node); - if (unlikely(!bh)) - return -ENOMEM; - - err = -EEXIST; /* internal code */ - page = bh->b_page; - - if (buffer_uptodate(bh) || buffer_dirty(bh)) - goto found; - - if (pblocknr == 0) { - pblocknr = blocknr; - if (inode->i_ino != NILFS_DAT_INO) { - struct the_nilfs *nilfs = inode->i_sb->s_fs_info; - - /* blocknr is a virtual block number */ - err = nilfs_dat_translate(nilfs->ns_dat, blocknr, - &pblocknr); - if (unlikely(err)) { - brelse(bh); - goto out_locked; - } - } - } - - if (mode == READA) { - if (pblocknr != *submit_ptr + 1 || !trylock_buffer(bh)) { - err = -EBUSY; /* internal code */ - brelse(bh); - goto out_locked; - } - } else { /* mode == READ */ - lock_buffer(bh); - } - if (buffer_uptodate(bh)) { - unlock_buffer(bh); - err = -EEXIST; /* internal code */ - goto found; - } - set_buffer_mapped(bh); - bh->b_bdev = inode->i_sb->s_bdev; - bh->b_blocknr = pblocknr; /* set block address for read */ - bh->b_end_io = end_buffer_read_sync; - get_bh(bh); - submit_bh(mode, bh); - bh->b_blocknr = blocknr; /* set back to the given block address */ - *submit_ptr = pblocknr; - err = 0; -found: - *pbh = bh; - -out_locked: - unlock_page(page); - page_cache_release(page); - return err; -} - -/** - * nilfs_btnode_delete - delete B-tree node buffer - * @bh: buffer to be deleted - * - * nilfs_btnode_delete() invalidates the specified buffer and delete the page - * including the buffer if the page gets unbusy. - */ -void nilfs_btnode_delete(struct buffer_head *bh) -{ - struct address_space *mapping; - struct page *page = bh->b_page; - pgoff_t index = page_index(page); - int still_dirty; - - page_cache_get(page); - lock_page(page); - wait_on_page_writeback(page); - - nilfs_forget_buffer(bh); - still_dirty = PageDirty(page); - mapping = page->mapping; - unlock_page(page); - page_cache_release(page); - - if (!still_dirty && mapping) - invalidate_inode_pages2_range(mapping, index, index); -} - -/** - * nilfs_btnode_prepare_change_key - * prepare to move contents of the block for old key to one of new key. - * the old buffer will not be removed, but might be reused for new buffer. - * it might return -ENOMEM because of memory allocation errors, - * and might return -EIO because of disk read errors. - */ -int nilfs_btnode_prepare_change_key(struct address_space *btnc, - struct nilfs_btnode_chkey_ctxt *ctxt) -{ - struct buffer_head *obh, *nbh; - struct inode *inode = NILFS_BTNC_I(btnc); - __u64 oldkey = ctxt->oldkey, newkey = ctxt->newkey; - int err; - - if (oldkey == newkey) - return 0; - - obh = ctxt->bh; - ctxt->newbh = NULL; - - if (inode->i_blkbits == PAGE_CACHE_SHIFT) { - lock_page(obh->b_page); - /* - * We cannot call radix_tree_preload for the kernels older - * than 2.6.23, because it is not exported for modules. - */ -retry: - err = radix_tree_preload(GFP_NOFS & ~__GFP_HIGHMEM); - if (err) - goto failed_unlock; - /* BUG_ON(oldkey != obh->b_page->index); */ - if (unlikely(oldkey != obh->b_page->index)) - NILFS_PAGE_BUG(obh->b_page, - "invalid oldkey %lld (newkey=%lld)", - (unsigned long long)oldkey, - (unsigned long long)newkey); - - spin_lock_irq(&btnc->tree_lock); - err = radix_tree_insert(&btnc->page_tree, newkey, obh->b_page); - spin_unlock_irq(&btnc->tree_lock); - /* - * Note: page->index will not change to newkey until - * nilfs_btnode_commit_change_key() will be called. - * To protect the page in intermediate state, the page lock - * is held. - */ - radix_tree_preload_end(); - if (!err) - return 0; - else if (err != -EEXIST) - goto failed_unlock; - - err = invalidate_inode_pages2_range(btnc, newkey, newkey); - if (!err) - goto retry; - /* fallback to copy mode */ - unlock_page(obh->b_page); - } - - nbh = nilfs_btnode_create_block(btnc, newkey); - if (!nbh) - return -ENOMEM; - - BUG_ON(nbh == obh); - ctxt->newbh = nbh; - return 0; - - failed_unlock: - unlock_page(obh->b_page); - return err; -} - -/** - * nilfs_btnode_commit_change_key - * commit the change_key operation prepared by prepare_change_key(). - */ -void nilfs_btnode_commit_change_key(struct address_space *btnc, - struct nilfs_btnode_chkey_ctxt *ctxt) -{ - struct buffer_head *obh = ctxt->bh, *nbh = ctxt->newbh; - __u64 oldkey = ctxt->oldkey, newkey = ctxt->newkey; - struct page *opage; - - if (oldkey == newkey) - return; - - if (nbh == NULL) { /* blocksize == pagesize */ - opage = obh->b_page; - if (unlikely(oldkey != opage->index)) - NILFS_PAGE_BUG(opage, - "invalid oldkey %lld (newkey=%lld)", - (unsigned long long)oldkey, - (unsigned long long)newkey); - mark_buffer_dirty(obh); - - spin_lock_irq(&btnc->tree_lock); - radix_tree_delete(&btnc->page_tree, oldkey); - radix_tree_tag_set(&btnc->page_tree, newkey, - PAGECACHE_TAG_DIRTY); - spin_unlock_irq(&btnc->tree_lock); - - opage->index = obh->b_blocknr = newkey; - unlock_page(opage); - } else { - nilfs_copy_buffer(nbh, obh); - mark_buffer_dirty(nbh); - - nbh->b_blocknr = newkey; - ctxt->bh = nbh; - nilfs_btnode_delete(obh); /* will decrement bh->b_count */ - } -} - -/** - * nilfs_btnode_abort_change_key - * abort the change_key operation prepared by prepare_change_key(). - */ -void nilfs_btnode_abort_change_key(struct address_space *btnc, - struct nilfs_btnode_chkey_ctxt *ctxt) -{ - struct buffer_head *nbh = ctxt->newbh; - __u64 oldkey = ctxt->oldkey, newkey = ctxt->newkey; - - if (oldkey == newkey) - return; - - if (nbh == NULL) { /* blocksize == pagesize */ - spin_lock_irq(&btnc->tree_lock); - radix_tree_delete(&btnc->page_tree, newkey); - spin_unlock_irq(&btnc->tree_lock); - unlock_page(ctxt->bh->b_page); - } else - brelse(nbh); -} diff --git a/ANDROID_3.4.5/fs/nilfs2/btnode.h b/ANDROID_3.4.5/fs/nilfs2/btnode.h deleted file mode 100644 index 3a4dd2d8..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/btnode.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * btnode.h - NILFS B-tree node cache - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Seiji Kihara <kihara@osrg.net> - * Revised by Ryusuke Konishi <ryusuke@osrg.net> - */ - -#ifndef _NILFS_BTNODE_H -#define _NILFS_BTNODE_H - -#include <linux/types.h> -#include <linux/buffer_head.h> -#include <linux/fs.h> -#include <linux/backing-dev.h> - - -struct nilfs_btnode_chkey_ctxt { - __u64 oldkey; - __u64 newkey; - struct buffer_head *bh; - struct buffer_head *newbh; -}; - -void nilfs_btnode_cache_clear(struct address_space *); -struct buffer_head *nilfs_btnode_create_block(struct address_space *btnc, - __u64 blocknr); -int nilfs_btnode_submit_block(struct address_space *, __u64, sector_t, int, - struct buffer_head **, sector_t *); -void nilfs_btnode_delete(struct buffer_head *); -int nilfs_btnode_prepare_change_key(struct address_space *, - struct nilfs_btnode_chkey_ctxt *); -void nilfs_btnode_commit_change_key(struct address_space *, - struct nilfs_btnode_chkey_ctxt *); -void nilfs_btnode_abort_change_key(struct address_space *, - struct nilfs_btnode_chkey_ctxt *); - -#endif /* _NILFS_BTNODE_H */ diff --git a/ANDROID_3.4.5/fs/nilfs2/btree.c b/ANDROID_3.4.5/fs/nilfs2/btree.c deleted file mode 100644 index b2e3ff34..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/btree.c +++ /dev/null @@ -1,2310 +0,0 @@ -/* - * btree.c - NILFS B-tree. - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Koji Sato <koji@osrg.net>. - */ - -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/pagevec.h> -#include "nilfs.h" -#include "page.h" -#include "btnode.h" -#include "btree.h" -#include "alloc.h" -#include "dat.h" - -static struct nilfs_btree_path *nilfs_btree_alloc_path(void) -{ - struct nilfs_btree_path *path; - int level = NILFS_BTREE_LEVEL_DATA; - - path = kmem_cache_alloc(nilfs_btree_path_cache, GFP_NOFS); - if (path == NULL) - goto out; - - for (; level < NILFS_BTREE_LEVEL_MAX; level++) { - path[level].bp_bh = NULL; - path[level].bp_sib_bh = NULL; - path[level].bp_index = 0; - path[level].bp_oldreq.bpr_ptr = NILFS_BMAP_INVALID_PTR; - path[level].bp_newreq.bpr_ptr = NILFS_BMAP_INVALID_PTR; - path[level].bp_op = NULL; - } - -out: - return path; -} - -static void nilfs_btree_free_path(struct nilfs_btree_path *path) -{ - int level = NILFS_BTREE_LEVEL_DATA; - - for (; level < NILFS_BTREE_LEVEL_MAX; level++) - brelse(path[level].bp_bh); - - kmem_cache_free(nilfs_btree_path_cache, path); -} - -/* - * B-tree node operations - */ -static int nilfs_btree_get_new_block(const struct nilfs_bmap *btree, - __u64 ptr, struct buffer_head **bhp) -{ - struct address_space *btnc = &NILFS_BMAP_I(btree)->i_btnode_cache; - struct buffer_head *bh; - - bh = nilfs_btnode_create_block(btnc, ptr); - if (!bh) - return -ENOMEM; - - set_buffer_nilfs_volatile(bh); - *bhp = bh; - return 0; -} - -static int nilfs_btree_node_get_flags(const struct nilfs_btree_node *node) -{ - return node->bn_flags; -} - -static void -nilfs_btree_node_set_flags(struct nilfs_btree_node *node, int flags) -{ - node->bn_flags = flags; -} - -static int nilfs_btree_node_root(const struct nilfs_btree_node *node) -{ - return nilfs_btree_node_get_flags(node) & NILFS_BTREE_NODE_ROOT; -} - -static int nilfs_btree_node_get_level(const struct nilfs_btree_node *node) -{ - return node->bn_level; -} - -static void -nilfs_btree_node_set_level(struct nilfs_btree_node *node, int level) -{ - node->bn_level = level; -} - -static int nilfs_btree_node_get_nchildren(const struct nilfs_btree_node *node) -{ - return le16_to_cpu(node->bn_nchildren); -} - -static void -nilfs_btree_node_set_nchildren(struct nilfs_btree_node *node, int nchildren) -{ - node->bn_nchildren = cpu_to_le16(nchildren); -} - -static int nilfs_btree_node_size(const struct nilfs_bmap *btree) -{ - return 1 << btree->b_inode->i_blkbits; -} - -static int nilfs_btree_nchildren_per_block(const struct nilfs_bmap *btree) -{ - return btree->b_nchildren_per_block; -} - -static __le64 * -nilfs_btree_node_dkeys(const struct nilfs_btree_node *node) -{ - return (__le64 *)((char *)(node + 1) + - (nilfs_btree_node_root(node) ? - 0 : NILFS_BTREE_NODE_EXTRA_PAD_SIZE)); -} - -static __le64 * -nilfs_btree_node_dptrs(const struct nilfs_btree_node *node, int ncmax) -{ - return (__le64 *)(nilfs_btree_node_dkeys(node) + ncmax); -} - -static __u64 -nilfs_btree_node_get_key(const struct nilfs_btree_node *node, int index) -{ - return le64_to_cpu(*(nilfs_btree_node_dkeys(node) + index)); -} - -static void -nilfs_btree_node_set_key(struct nilfs_btree_node *node, int index, __u64 key) -{ - *(nilfs_btree_node_dkeys(node) + index) = cpu_to_le64(key); -} - -static __u64 -nilfs_btree_node_get_ptr(const struct nilfs_btree_node *node, int index, - int ncmax) -{ - return le64_to_cpu(*(nilfs_btree_node_dptrs(node, ncmax) + index)); -} - -static void -nilfs_btree_node_set_ptr(struct nilfs_btree_node *node, int index, __u64 ptr, - int ncmax) -{ - *(nilfs_btree_node_dptrs(node, ncmax) + index) = cpu_to_le64(ptr); -} - -static void nilfs_btree_node_init(struct nilfs_btree_node *node, int flags, - int level, int nchildren, int ncmax, - const __u64 *keys, const __u64 *ptrs) -{ - __le64 *dkeys; - __le64 *dptrs; - int i; - - nilfs_btree_node_set_flags(node, flags); - nilfs_btree_node_set_level(node, level); - nilfs_btree_node_set_nchildren(node, nchildren); - - dkeys = nilfs_btree_node_dkeys(node); - dptrs = nilfs_btree_node_dptrs(node, ncmax); - for (i = 0; i < nchildren; i++) { - dkeys[i] = cpu_to_le64(keys[i]); - dptrs[i] = cpu_to_le64(ptrs[i]); - } -} - -/* Assume the buffer heads corresponding to left and right are locked. */ -static void nilfs_btree_node_move_left(struct nilfs_btree_node *left, - struct nilfs_btree_node *right, - int n, int lncmax, int rncmax) -{ - __le64 *ldkeys, *rdkeys; - __le64 *ldptrs, *rdptrs; - int lnchildren, rnchildren; - - ldkeys = nilfs_btree_node_dkeys(left); - ldptrs = nilfs_btree_node_dptrs(left, lncmax); - lnchildren = nilfs_btree_node_get_nchildren(left); - - rdkeys = nilfs_btree_node_dkeys(right); - rdptrs = nilfs_btree_node_dptrs(right, rncmax); - rnchildren = nilfs_btree_node_get_nchildren(right); - - memcpy(ldkeys + lnchildren, rdkeys, n * sizeof(*rdkeys)); - memcpy(ldptrs + lnchildren, rdptrs, n * sizeof(*rdptrs)); - memmove(rdkeys, rdkeys + n, (rnchildren - n) * sizeof(*rdkeys)); - memmove(rdptrs, rdptrs + n, (rnchildren - n) * sizeof(*rdptrs)); - - lnchildren += n; - rnchildren -= n; - nilfs_btree_node_set_nchildren(left, lnchildren); - nilfs_btree_node_set_nchildren(right, rnchildren); -} - -/* Assume that the buffer heads corresponding to left and right are locked. */ -static void nilfs_btree_node_move_right(struct nilfs_btree_node *left, - struct nilfs_btree_node *right, - int n, int lncmax, int rncmax) -{ - __le64 *ldkeys, *rdkeys; - __le64 *ldptrs, *rdptrs; - int lnchildren, rnchildren; - - ldkeys = nilfs_btree_node_dkeys(left); - ldptrs = nilfs_btree_node_dptrs(left, lncmax); - lnchildren = nilfs_btree_node_get_nchildren(left); - - rdkeys = nilfs_btree_node_dkeys(right); - rdptrs = nilfs_btree_node_dptrs(right, rncmax); - rnchildren = nilfs_btree_node_get_nchildren(right); - - memmove(rdkeys + n, rdkeys, rnchildren * sizeof(*rdkeys)); - memmove(rdptrs + n, rdptrs, rnchildren * sizeof(*rdptrs)); - memcpy(rdkeys, ldkeys + lnchildren - n, n * sizeof(*rdkeys)); - memcpy(rdptrs, ldptrs + lnchildren - n, n * sizeof(*rdptrs)); - - lnchildren -= n; - rnchildren += n; - nilfs_btree_node_set_nchildren(left, lnchildren); - nilfs_btree_node_set_nchildren(right, rnchildren); -} - -/* Assume that the buffer head corresponding to node is locked. */ -static void nilfs_btree_node_insert(struct nilfs_btree_node *node, int index, - __u64 key, __u64 ptr, int ncmax) -{ - __le64 *dkeys; - __le64 *dptrs; - int nchildren; - - dkeys = nilfs_btree_node_dkeys(node); - dptrs = nilfs_btree_node_dptrs(node, ncmax); - nchildren = nilfs_btree_node_get_nchildren(node); - if (index < nchildren) { - memmove(dkeys + index + 1, dkeys + index, - (nchildren - index) * sizeof(*dkeys)); - memmove(dptrs + index + 1, dptrs + index, - (nchildren - index) * sizeof(*dptrs)); - } - dkeys[index] = cpu_to_le64(key); - dptrs[index] = cpu_to_le64(ptr); - nchildren++; - nilfs_btree_node_set_nchildren(node, nchildren); -} - -/* Assume that the buffer head corresponding to node is locked. */ -static void nilfs_btree_node_delete(struct nilfs_btree_node *node, int index, - __u64 *keyp, __u64 *ptrp, int ncmax) -{ - __u64 key; - __u64 ptr; - __le64 *dkeys; - __le64 *dptrs; - int nchildren; - - dkeys = nilfs_btree_node_dkeys(node); - dptrs = nilfs_btree_node_dptrs(node, ncmax); - key = le64_to_cpu(dkeys[index]); - ptr = le64_to_cpu(dptrs[index]); - nchildren = nilfs_btree_node_get_nchildren(node); - if (keyp != NULL) - *keyp = key; - if (ptrp != NULL) - *ptrp = ptr; - - if (index < nchildren - 1) { - memmove(dkeys + index, dkeys + index + 1, - (nchildren - index - 1) * sizeof(*dkeys)); - memmove(dptrs + index, dptrs + index + 1, - (nchildren - index - 1) * sizeof(*dptrs)); - } - nchildren--; - nilfs_btree_node_set_nchildren(node, nchildren); -} - -static int nilfs_btree_node_lookup(const struct nilfs_btree_node *node, - __u64 key, int *indexp) -{ - __u64 nkey; - int index, low, high, s; - - /* binary search */ - low = 0; - high = nilfs_btree_node_get_nchildren(node) - 1; - index = 0; - s = 0; - while (low <= high) { - index = (low + high) / 2; - nkey = nilfs_btree_node_get_key(node, index); - if (nkey == key) { - s = 0; - goto out; - } else if (nkey < key) { - low = index + 1; - s = -1; - } else { - high = index - 1; - s = 1; - } - } - - /* adjust index */ - if (nilfs_btree_node_get_level(node) > NILFS_BTREE_LEVEL_NODE_MIN) { - if (s > 0 && index > 0) - index--; - } else if (s < 0) - index++; - - out: - *indexp = index; - - return s == 0; -} - -/** - * nilfs_btree_node_broken - verify consistency of btree node - * @node: btree node block to be examined - * @size: node size (in bytes) - * @blocknr: block number - * - * Return Value: If node is broken, 1 is returned. Otherwise, 0 is returned. - */ -static int nilfs_btree_node_broken(const struct nilfs_btree_node *node, - size_t size, sector_t blocknr) -{ - int level, flags, nchildren; - int ret = 0; - - level = nilfs_btree_node_get_level(node); - flags = nilfs_btree_node_get_flags(node); - nchildren = nilfs_btree_node_get_nchildren(node); - - if (unlikely(level < NILFS_BTREE_LEVEL_NODE_MIN || - level >= NILFS_BTREE_LEVEL_MAX || - (flags & NILFS_BTREE_NODE_ROOT) || - nchildren < 0 || - nchildren > NILFS_BTREE_NODE_NCHILDREN_MAX(size))) { - printk(KERN_CRIT "NILFS: bad btree node (blocknr=%llu): " - "level = %d, flags = 0x%x, nchildren = %d\n", - (unsigned long long)blocknr, level, flags, nchildren); - ret = 1; - } - return ret; -} - -int nilfs_btree_broken_node_block(struct buffer_head *bh) -{ - int ret; - - if (buffer_nilfs_checked(bh)) - return 0; - - ret = nilfs_btree_node_broken((struct nilfs_btree_node *)bh->b_data, - bh->b_size, bh->b_blocknr); - if (likely(!ret)) - set_buffer_nilfs_checked(bh); - return ret; -} - -static struct nilfs_btree_node * -nilfs_btree_get_root(const struct nilfs_bmap *btree) -{ - return (struct nilfs_btree_node *)btree->b_u.u_data; -} - -static struct nilfs_btree_node * -nilfs_btree_get_nonroot_node(const struct nilfs_btree_path *path, int level) -{ - return (struct nilfs_btree_node *)path[level].bp_bh->b_data; -} - -static struct nilfs_btree_node * -nilfs_btree_get_sib_node(const struct nilfs_btree_path *path, int level) -{ - return (struct nilfs_btree_node *)path[level].bp_sib_bh->b_data; -} - -static int nilfs_btree_height(const struct nilfs_bmap *btree) -{ - return nilfs_btree_node_get_level(nilfs_btree_get_root(btree)) + 1; -} - -static struct nilfs_btree_node * -nilfs_btree_get_node(const struct nilfs_bmap *btree, - const struct nilfs_btree_path *path, - int level, int *ncmaxp) -{ - struct nilfs_btree_node *node; - - if (level == nilfs_btree_height(btree) - 1) { - node = nilfs_btree_get_root(btree); - *ncmaxp = NILFS_BTREE_ROOT_NCHILDREN_MAX; - } else { - node = nilfs_btree_get_nonroot_node(path, level); - *ncmaxp = nilfs_btree_nchildren_per_block(btree); - } - return node; -} - -static int -nilfs_btree_bad_node(struct nilfs_btree_node *node, int level) -{ - if (unlikely(nilfs_btree_node_get_level(node) != level)) { - dump_stack(); - printk(KERN_CRIT "NILFS: btree level mismatch: %d != %d\n", - nilfs_btree_node_get_level(node), level); - return 1; - } - return 0; -} - -struct nilfs_btree_readahead_info { - struct nilfs_btree_node *node; /* parent node */ - int max_ra_blocks; /* max nof blocks to read ahead */ - int index; /* current index on the parent node */ - int ncmax; /* nof children in the parent node */ -}; - -static int __nilfs_btree_get_block(const struct nilfs_bmap *btree, __u64 ptr, - struct buffer_head **bhp, - const struct nilfs_btree_readahead_info *ra) -{ - struct address_space *btnc = &NILFS_BMAP_I(btree)->i_btnode_cache; - struct buffer_head *bh, *ra_bh; - sector_t submit_ptr = 0; - int ret; - - ret = nilfs_btnode_submit_block(btnc, ptr, 0, READ, &bh, &submit_ptr); - if (ret) { - if (ret != -EEXIST) - return ret; - goto out_check; - } - - if (ra) { - int i, n; - __u64 ptr2; - - /* read ahead sibling nodes */ - for (n = ra->max_ra_blocks, i = ra->index + 1; - n > 0 && i < ra->ncmax; n--, i++) { - ptr2 = nilfs_btree_node_get_ptr(ra->node, i, ra->ncmax); - - ret = nilfs_btnode_submit_block(btnc, ptr2, 0, READA, - &ra_bh, &submit_ptr); - if (likely(!ret || ret == -EEXIST)) - brelse(ra_bh); - else if (ret != -EBUSY) - break; - if (!buffer_locked(bh)) - goto out_no_wait; - } - } - - wait_on_buffer(bh); - - out_no_wait: - if (!buffer_uptodate(bh)) { - brelse(bh); - return -EIO; - } - - out_check: - if (nilfs_btree_broken_node_block(bh)) { - clear_buffer_uptodate(bh); - brelse(bh); - return -EINVAL; - } - - *bhp = bh; - return 0; -} - -static int nilfs_btree_get_block(const struct nilfs_bmap *btree, __u64 ptr, - struct buffer_head **bhp) -{ - return __nilfs_btree_get_block(btree, ptr, bhp, NULL); -} - -static int nilfs_btree_do_lookup(const struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - __u64 key, __u64 *ptrp, int minlevel, - int readahead) -{ - struct nilfs_btree_node *node; - struct nilfs_btree_readahead_info p, *ra; - __u64 ptr; - int level, index, found, ncmax, ret; - - node = nilfs_btree_get_root(btree); - level = nilfs_btree_node_get_level(node); - if (level < minlevel || nilfs_btree_node_get_nchildren(node) <= 0) - return -ENOENT; - - found = nilfs_btree_node_lookup(node, key, &index); - ptr = nilfs_btree_node_get_ptr(node, index, - NILFS_BTREE_ROOT_NCHILDREN_MAX); - path[level].bp_bh = NULL; - path[level].bp_index = index; - - ncmax = nilfs_btree_nchildren_per_block(btree); - - while (--level >= minlevel) { - ra = NULL; - if (level == NILFS_BTREE_LEVEL_NODE_MIN && readahead) { - p.node = nilfs_btree_get_node(btree, path, level + 1, - &p.ncmax); - p.index = index; - p.max_ra_blocks = 7; - ra = &p; - } - ret = __nilfs_btree_get_block(btree, ptr, &path[level].bp_bh, - ra); - if (ret < 0) - return ret; - - node = nilfs_btree_get_nonroot_node(path, level); - if (nilfs_btree_bad_node(node, level)) - return -EINVAL; - if (!found) - found = nilfs_btree_node_lookup(node, key, &index); - else - index = 0; - if (index < ncmax) { - ptr = nilfs_btree_node_get_ptr(node, index, ncmax); - } else { - WARN_ON(found || level != NILFS_BTREE_LEVEL_NODE_MIN); - /* insert */ - ptr = NILFS_BMAP_INVALID_PTR; - } - path[level].bp_index = index; - } - if (!found) - return -ENOENT; - - if (ptrp != NULL) - *ptrp = ptr; - - return 0; -} - -static int nilfs_btree_do_lookup_last(const struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - __u64 *keyp, __u64 *ptrp) -{ - struct nilfs_btree_node *node; - __u64 ptr; - int index, level, ncmax, ret; - - node = nilfs_btree_get_root(btree); - index = nilfs_btree_node_get_nchildren(node) - 1; - if (index < 0) - return -ENOENT; - level = nilfs_btree_node_get_level(node); - ptr = nilfs_btree_node_get_ptr(node, index, - NILFS_BTREE_ROOT_NCHILDREN_MAX); - path[level].bp_bh = NULL; - path[level].bp_index = index; - ncmax = nilfs_btree_nchildren_per_block(btree); - - for (level--; level > 0; level--) { - ret = nilfs_btree_get_block(btree, ptr, &path[level].bp_bh); - if (ret < 0) - return ret; - node = nilfs_btree_get_nonroot_node(path, level); - if (nilfs_btree_bad_node(node, level)) - return -EINVAL; - index = nilfs_btree_node_get_nchildren(node) - 1; - ptr = nilfs_btree_node_get_ptr(node, index, ncmax); - path[level].bp_index = index; - } - - if (keyp != NULL) - *keyp = nilfs_btree_node_get_key(node, index); - if (ptrp != NULL) - *ptrp = ptr; - - return 0; -} - -static int nilfs_btree_lookup(const struct nilfs_bmap *btree, - __u64 key, int level, __u64 *ptrp) -{ - struct nilfs_btree_path *path; - int ret; - - path = nilfs_btree_alloc_path(); - if (path == NULL) - return -ENOMEM; - - ret = nilfs_btree_do_lookup(btree, path, key, ptrp, level, 0); - - nilfs_btree_free_path(path); - - return ret; -} - -static int nilfs_btree_lookup_contig(const struct nilfs_bmap *btree, - __u64 key, __u64 *ptrp, unsigned maxblocks) -{ - struct nilfs_btree_path *path; - struct nilfs_btree_node *node; - struct inode *dat = NULL; - __u64 ptr, ptr2; - sector_t blocknr; - int level = NILFS_BTREE_LEVEL_NODE_MIN; - int ret, cnt, index, maxlevel, ncmax; - struct nilfs_btree_readahead_info p; - - path = nilfs_btree_alloc_path(); - if (path == NULL) - return -ENOMEM; - - ret = nilfs_btree_do_lookup(btree, path, key, &ptr, level, 1); - if (ret < 0) - goto out; - - if (NILFS_BMAP_USE_VBN(btree)) { - dat = nilfs_bmap_get_dat(btree); - ret = nilfs_dat_translate(dat, ptr, &blocknr); - if (ret < 0) - goto out; - ptr = blocknr; - } - cnt = 1; - if (cnt == maxblocks) - goto end; - - maxlevel = nilfs_btree_height(btree) - 1; - node = nilfs_btree_get_node(btree, path, level, &ncmax); - index = path[level].bp_index + 1; - for (;;) { - while (index < nilfs_btree_node_get_nchildren(node)) { - if (nilfs_btree_node_get_key(node, index) != - key + cnt) - goto end; - ptr2 = nilfs_btree_node_get_ptr(node, index, ncmax); - if (dat) { - ret = nilfs_dat_translate(dat, ptr2, &blocknr); - if (ret < 0) - goto out; - ptr2 = blocknr; - } - if (ptr2 != ptr + cnt || ++cnt == maxblocks) - goto end; - index++; - continue; - } - if (level == maxlevel) - break; - - /* look-up right sibling node */ - p.node = nilfs_btree_get_node(btree, path, level + 1, &p.ncmax); - p.index = path[level + 1].bp_index + 1; - p.max_ra_blocks = 7; - if (p.index >= nilfs_btree_node_get_nchildren(p.node) || - nilfs_btree_node_get_key(p.node, p.index) != key + cnt) - break; - ptr2 = nilfs_btree_node_get_ptr(p.node, p.index, p.ncmax); - path[level + 1].bp_index = p.index; - - brelse(path[level].bp_bh); - path[level].bp_bh = NULL; - - ret = __nilfs_btree_get_block(btree, ptr2, &path[level].bp_bh, - &p); - if (ret < 0) - goto out; - node = nilfs_btree_get_nonroot_node(path, level); - ncmax = nilfs_btree_nchildren_per_block(btree); - index = 0; - path[level].bp_index = index; - } - end: - *ptrp = ptr; - ret = cnt; - out: - nilfs_btree_free_path(path); - return ret; -} - -static void nilfs_btree_promote_key(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, __u64 key) -{ - if (level < nilfs_btree_height(btree) - 1) { - do { - nilfs_btree_node_set_key( - nilfs_btree_get_nonroot_node(path, level), - path[level].bp_index, key); - if (!buffer_dirty(path[level].bp_bh)) - mark_buffer_dirty(path[level].bp_bh); - } while ((path[level].bp_index == 0) && - (++level < nilfs_btree_height(btree) - 1)); - } - - /* root */ - if (level == nilfs_btree_height(btree) - 1) { - nilfs_btree_node_set_key(nilfs_btree_get_root(btree), - path[level].bp_index, key); - } -} - -static void nilfs_btree_do_insert(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, __u64 *keyp, __u64 *ptrp) -{ - struct nilfs_btree_node *node; - int ncblk; - - if (level < nilfs_btree_height(btree) - 1) { - node = nilfs_btree_get_nonroot_node(path, level); - ncblk = nilfs_btree_nchildren_per_block(btree); - nilfs_btree_node_insert(node, path[level].bp_index, - *keyp, *ptrp, ncblk); - if (!buffer_dirty(path[level].bp_bh)) - mark_buffer_dirty(path[level].bp_bh); - - if (path[level].bp_index == 0) - nilfs_btree_promote_key(btree, path, level + 1, - nilfs_btree_node_get_key(node, - 0)); - } else { - node = nilfs_btree_get_root(btree); - nilfs_btree_node_insert(node, path[level].bp_index, - *keyp, *ptrp, - NILFS_BTREE_ROOT_NCHILDREN_MAX); - } -} - -static void nilfs_btree_carry_left(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, __u64 *keyp, __u64 *ptrp) -{ - struct nilfs_btree_node *node, *left; - int nchildren, lnchildren, n, move, ncblk; - - node = nilfs_btree_get_nonroot_node(path, level); - left = nilfs_btree_get_sib_node(path, level); - nchildren = nilfs_btree_node_get_nchildren(node); - lnchildren = nilfs_btree_node_get_nchildren(left); - ncblk = nilfs_btree_nchildren_per_block(btree); - move = 0; - - n = (nchildren + lnchildren + 1) / 2 - lnchildren; - if (n > path[level].bp_index) { - /* move insert point */ - n--; - move = 1; - } - - nilfs_btree_node_move_left(left, node, n, ncblk, ncblk); - - if (!buffer_dirty(path[level].bp_bh)) - mark_buffer_dirty(path[level].bp_bh); - if (!buffer_dirty(path[level].bp_sib_bh)) - mark_buffer_dirty(path[level].bp_sib_bh); - - nilfs_btree_promote_key(btree, path, level + 1, - nilfs_btree_node_get_key(node, 0)); - - if (move) { - brelse(path[level].bp_bh); - path[level].bp_bh = path[level].bp_sib_bh; - path[level].bp_sib_bh = NULL; - path[level].bp_index += lnchildren; - path[level + 1].bp_index--; - } else { - brelse(path[level].bp_sib_bh); - path[level].bp_sib_bh = NULL; - path[level].bp_index -= n; - } - - nilfs_btree_do_insert(btree, path, level, keyp, ptrp); -} - -static void nilfs_btree_carry_right(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, __u64 *keyp, __u64 *ptrp) -{ - struct nilfs_btree_node *node, *right; - int nchildren, rnchildren, n, move, ncblk; - - node = nilfs_btree_get_nonroot_node(path, level); - right = nilfs_btree_get_sib_node(path, level); - nchildren = nilfs_btree_node_get_nchildren(node); - rnchildren = nilfs_btree_node_get_nchildren(right); - ncblk = nilfs_btree_nchildren_per_block(btree); - move = 0; - - n = (nchildren + rnchildren + 1) / 2 - rnchildren; - if (n > nchildren - path[level].bp_index) { - /* move insert point */ - n--; - move = 1; - } - - nilfs_btree_node_move_right(node, right, n, ncblk, ncblk); - - if (!buffer_dirty(path[level].bp_bh)) - mark_buffer_dirty(path[level].bp_bh); - if (!buffer_dirty(path[level].bp_sib_bh)) - mark_buffer_dirty(path[level].bp_sib_bh); - - path[level + 1].bp_index++; - nilfs_btree_promote_key(btree, path, level + 1, - nilfs_btree_node_get_key(right, 0)); - path[level + 1].bp_index--; - - if (move) { - brelse(path[level].bp_bh); - path[level].bp_bh = path[level].bp_sib_bh; - path[level].bp_sib_bh = NULL; - path[level].bp_index -= nilfs_btree_node_get_nchildren(node); - path[level + 1].bp_index++; - } else { - brelse(path[level].bp_sib_bh); - path[level].bp_sib_bh = NULL; - } - - nilfs_btree_do_insert(btree, path, level, keyp, ptrp); -} - -static void nilfs_btree_split(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, __u64 *keyp, __u64 *ptrp) -{ - struct nilfs_btree_node *node, *right; - __u64 newkey; - __u64 newptr; - int nchildren, n, move, ncblk; - - node = nilfs_btree_get_nonroot_node(path, level); - right = nilfs_btree_get_sib_node(path, level); - nchildren = nilfs_btree_node_get_nchildren(node); - ncblk = nilfs_btree_nchildren_per_block(btree); - move = 0; - - n = (nchildren + 1) / 2; - if (n > nchildren - path[level].bp_index) { - n--; - move = 1; - } - - nilfs_btree_node_move_right(node, right, n, ncblk, ncblk); - - if (!buffer_dirty(path[level].bp_bh)) - mark_buffer_dirty(path[level].bp_bh); - if (!buffer_dirty(path[level].bp_sib_bh)) - mark_buffer_dirty(path[level].bp_sib_bh); - - newkey = nilfs_btree_node_get_key(right, 0); - newptr = path[level].bp_newreq.bpr_ptr; - - if (move) { - path[level].bp_index -= nilfs_btree_node_get_nchildren(node); - nilfs_btree_node_insert(right, path[level].bp_index, - *keyp, *ptrp, ncblk); - - *keyp = nilfs_btree_node_get_key(right, 0); - *ptrp = path[level].bp_newreq.bpr_ptr; - - brelse(path[level].bp_bh); - path[level].bp_bh = path[level].bp_sib_bh; - path[level].bp_sib_bh = NULL; - } else { - nilfs_btree_do_insert(btree, path, level, keyp, ptrp); - - *keyp = nilfs_btree_node_get_key(right, 0); - *ptrp = path[level].bp_newreq.bpr_ptr; - - brelse(path[level].bp_sib_bh); - path[level].bp_sib_bh = NULL; - } - - path[level + 1].bp_index++; -} - -static void nilfs_btree_grow(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, __u64 *keyp, __u64 *ptrp) -{ - struct nilfs_btree_node *root, *child; - int n, ncblk; - - root = nilfs_btree_get_root(btree); - child = nilfs_btree_get_sib_node(path, level); - ncblk = nilfs_btree_nchildren_per_block(btree); - - n = nilfs_btree_node_get_nchildren(root); - - nilfs_btree_node_move_right(root, child, n, - NILFS_BTREE_ROOT_NCHILDREN_MAX, ncblk); - nilfs_btree_node_set_level(root, level + 1); - - if (!buffer_dirty(path[level].bp_sib_bh)) - mark_buffer_dirty(path[level].bp_sib_bh); - - path[level].bp_bh = path[level].bp_sib_bh; - path[level].bp_sib_bh = NULL; - - nilfs_btree_do_insert(btree, path, level, keyp, ptrp); - - *keyp = nilfs_btree_node_get_key(child, 0); - *ptrp = path[level].bp_newreq.bpr_ptr; -} - -static __u64 nilfs_btree_find_near(const struct nilfs_bmap *btree, - const struct nilfs_btree_path *path) -{ - struct nilfs_btree_node *node; - int level, ncmax; - - if (path == NULL) - return NILFS_BMAP_INVALID_PTR; - - /* left sibling */ - level = NILFS_BTREE_LEVEL_NODE_MIN; - if (path[level].bp_index > 0) { - node = nilfs_btree_get_node(btree, path, level, &ncmax); - return nilfs_btree_node_get_ptr(node, - path[level].bp_index - 1, - ncmax); - } - - /* parent */ - level = NILFS_BTREE_LEVEL_NODE_MIN + 1; - if (level <= nilfs_btree_height(btree) - 1) { - node = nilfs_btree_get_node(btree, path, level, &ncmax); - return nilfs_btree_node_get_ptr(node, path[level].bp_index, - ncmax); - } - - return NILFS_BMAP_INVALID_PTR; -} - -static __u64 nilfs_btree_find_target_v(const struct nilfs_bmap *btree, - const struct nilfs_btree_path *path, - __u64 key) -{ - __u64 ptr; - - ptr = nilfs_bmap_find_target_seq(btree, key); - if (ptr != NILFS_BMAP_INVALID_PTR) - /* sequential access */ - return ptr; - else { - ptr = nilfs_btree_find_near(btree, path); - if (ptr != NILFS_BMAP_INVALID_PTR) - /* near */ - return ptr; - } - /* block group */ - return nilfs_bmap_find_target_in_group(btree); -} - -static int nilfs_btree_prepare_insert(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int *levelp, __u64 key, __u64 ptr, - struct nilfs_bmap_stats *stats) -{ - struct buffer_head *bh; - struct nilfs_btree_node *node, *parent, *sib; - __u64 sibptr; - int pindex, level, ncmax, ncblk, ret; - struct inode *dat = NULL; - - stats->bs_nblocks = 0; - level = NILFS_BTREE_LEVEL_DATA; - - /* allocate a new ptr for data block */ - if (NILFS_BMAP_USE_VBN(btree)) { - path[level].bp_newreq.bpr_ptr = - nilfs_btree_find_target_v(btree, path, key); - dat = nilfs_bmap_get_dat(btree); - } - - ret = nilfs_bmap_prepare_alloc_ptr(btree, &path[level].bp_newreq, dat); - if (ret < 0) - goto err_out_data; - - ncblk = nilfs_btree_nchildren_per_block(btree); - - for (level = NILFS_BTREE_LEVEL_NODE_MIN; - level < nilfs_btree_height(btree) - 1; - level++) { - node = nilfs_btree_get_nonroot_node(path, level); - if (nilfs_btree_node_get_nchildren(node) < ncblk) { - path[level].bp_op = nilfs_btree_do_insert; - stats->bs_nblocks++; - goto out; - } - - parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax); - pindex = path[level + 1].bp_index; - - /* left sibling */ - if (pindex > 0) { - sibptr = nilfs_btree_node_get_ptr(parent, pindex - 1, - ncmax); - ret = nilfs_btree_get_block(btree, sibptr, &bh); - if (ret < 0) - goto err_out_child_node; - sib = (struct nilfs_btree_node *)bh->b_data; - if (nilfs_btree_node_get_nchildren(sib) < ncblk) { - path[level].bp_sib_bh = bh; - path[level].bp_op = nilfs_btree_carry_left; - stats->bs_nblocks++; - goto out; - } else { - brelse(bh); - } - } - - /* right sibling */ - if (pindex < nilfs_btree_node_get_nchildren(parent) - 1) { - sibptr = nilfs_btree_node_get_ptr(parent, pindex + 1, - ncmax); - ret = nilfs_btree_get_block(btree, sibptr, &bh); - if (ret < 0) - goto err_out_child_node; - sib = (struct nilfs_btree_node *)bh->b_data; - if (nilfs_btree_node_get_nchildren(sib) < ncblk) { - path[level].bp_sib_bh = bh; - path[level].bp_op = nilfs_btree_carry_right; - stats->bs_nblocks++; - goto out; - } else { - brelse(bh); - } - } - - /* split */ - path[level].bp_newreq.bpr_ptr = - path[level - 1].bp_newreq.bpr_ptr + 1; - ret = nilfs_bmap_prepare_alloc_ptr(btree, - &path[level].bp_newreq, dat); - if (ret < 0) - goto err_out_child_node; - ret = nilfs_btree_get_new_block(btree, - path[level].bp_newreq.bpr_ptr, - &bh); - if (ret < 0) - goto err_out_curr_node; - - stats->bs_nblocks++; - - sib = (struct nilfs_btree_node *)bh->b_data; - nilfs_btree_node_init(sib, 0, level, 0, ncblk, NULL, NULL); - path[level].bp_sib_bh = bh; - path[level].bp_op = nilfs_btree_split; - } - - /* root */ - node = nilfs_btree_get_root(btree); - if (nilfs_btree_node_get_nchildren(node) < - NILFS_BTREE_ROOT_NCHILDREN_MAX) { - path[level].bp_op = nilfs_btree_do_insert; - stats->bs_nblocks++; - goto out; - } - - /* grow */ - path[level].bp_newreq.bpr_ptr = path[level - 1].bp_newreq.bpr_ptr + 1; - ret = nilfs_bmap_prepare_alloc_ptr(btree, &path[level].bp_newreq, dat); - if (ret < 0) - goto err_out_child_node; - ret = nilfs_btree_get_new_block(btree, path[level].bp_newreq.bpr_ptr, - &bh); - if (ret < 0) - goto err_out_curr_node; - - nilfs_btree_node_init((struct nilfs_btree_node *)bh->b_data, - 0, level, 0, ncblk, NULL, NULL); - path[level].bp_sib_bh = bh; - path[level].bp_op = nilfs_btree_grow; - - level++; - path[level].bp_op = nilfs_btree_do_insert; - - /* a newly-created node block and a data block are added */ - stats->bs_nblocks += 2; - - /* success */ - out: - *levelp = level; - return ret; - - /* error */ - err_out_curr_node: - nilfs_bmap_abort_alloc_ptr(btree, &path[level].bp_newreq, dat); - err_out_child_node: - for (level--; level > NILFS_BTREE_LEVEL_DATA; level--) { - nilfs_btnode_delete(path[level].bp_sib_bh); - nilfs_bmap_abort_alloc_ptr(btree, &path[level].bp_newreq, dat); - - } - - nilfs_bmap_abort_alloc_ptr(btree, &path[level].bp_newreq, dat); - err_out_data: - *levelp = level; - stats->bs_nblocks = 0; - return ret; -} - -static void nilfs_btree_commit_insert(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int maxlevel, __u64 key, __u64 ptr) -{ - struct inode *dat = NULL; - int level; - - set_buffer_nilfs_volatile((struct buffer_head *)((unsigned long)ptr)); - ptr = path[NILFS_BTREE_LEVEL_DATA].bp_newreq.bpr_ptr; - if (NILFS_BMAP_USE_VBN(btree)) { - nilfs_bmap_set_target_v(btree, key, ptr); - dat = nilfs_bmap_get_dat(btree); - } - - for (level = NILFS_BTREE_LEVEL_NODE_MIN; level <= maxlevel; level++) { - nilfs_bmap_commit_alloc_ptr(btree, - &path[level - 1].bp_newreq, dat); - path[level].bp_op(btree, path, level, &key, &ptr); - } - - if (!nilfs_bmap_dirty(btree)) - nilfs_bmap_set_dirty(btree); -} - -static int nilfs_btree_insert(struct nilfs_bmap *btree, __u64 key, __u64 ptr) -{ - struct nilfs_btree_path *path; - struct nilfs_bmap_stats stats; - int level, ret; - - path = nilfs_btree_alloc_path(); - if (path == NULL) - return -ENOMEM; - - ret = nilfs_btree_do_lookup(btree, path, key, NULL, - NILFS_BTREE_LEVEL_NODE_MIN, 0); - if (ret != -ENOENT) { - if (ret == 0) - ret = -EEXIST; - goto out; - } - - ret = nilfs_btree_prepare_insert(btree, path, &level, key, ptr, &stats); - if (ret < 0) - goto out; - nilfs_btree_commit_insert(btree, path, level, key, ptr); - nilfs_inode_add_blocks(btree->b_inode, stats.bs_nblocks); - - out: - nilfs_btree_free_path(path); - return ret; -} - -static void nilfs_btree_do_delete(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, __u64 *keyp, __u64 *ptrp) -{ - struct nilfs_btree_node *node; - int ncblk; - - if (level < nilfs_btree_height(btree) - 1) { - node = nilfs_btree_get_nonroot_node(path, level); - ncblk = nilfs_btree_nchildren_per_block(btree); - nilfs_btree_node_delete(node, path[level].bp_index, - keyp, ptrp, ncblk); - if (!buffer_dirty(path[level].bp_bh)) - mark_buffer_dirty(path[level].bp_bh); - if (path[level].bp_index == 0) - nilfs_btree_promote_key(btree, path, level + 1, - nilfs_btree_node_get_key(node, 0)); - } else { - node = nilfs_btree_get_root(btree); - nilfs_btree_node_delete(node, path[level].bp_index, - keyp, ptrp, - NILFS_BTREE_ROOT_NCHILDREN_MAX); - } -} - -static void nilfs_btree_borrow_left(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, __u64 *keyp, __u64 *ptrp) -{ - struct nilfs_btree_node *node, *left; - int nchildren, lnchildren, n, ncblk; - - nilfs_btree_do_delete(btree, path, level, keyp, ptrp); - - node = nilfs_btree_get_nonroot_node(path, level); - left = nilfs_btree_get_sib_node(path, level); - nchildren = nilfs_btree_node_get_nchildren(node); - lnchildren = nilfs_btree_node_get_nchildren(left); - ncblk = nilfs_btree_nchildren_per_block(btree); - - n = (nchildren + lnchildren) / 2 - nchildren; - - nilfs_btree_node_move_right(left, node, n, ncblk, ncblk); - - if (!buffer_dirty(path[level].bp_bh)) - mark_buffer_dirty(path[level].bp_bh); - if (!buffer_dirty(path[level].bp_sib_bh)) - mark_buffer_dirty(path[level].bp_sib_bh); - - nilfs_btree_promote_key(btree, path, level + 1, - nilfs_btree_node_get_key(node, 0)); - - brelse(path[level].bp_sib_bh); - path[level].bp_sib_bh = NULL; - path[level].bp_index += n; -} - -static void nilfs_btree_borrow_right(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, __u64 *keyp, __u64 *ptrp) -{ - struct nilfs_btree_node *node, *right; - int nchildren, rnchildren, n, ncblk; - - nilfs_btree_do_delete(btree, path, level, keyp, ptrp); - - node = nilfs_btree_get_nonroot_node(path, level); - right = nilfs_btree_get_sib_node(path, level); - nchildren = nilfs_btree_node_get_nchildren(node); - rnchildren = nilfs_btree_node_get_nchildren(right); - ncblk = nilfs_btree_nchildren_per_block(btree); - - n = (nchildren + rnchildren) / 2 - nchildren; - - nilfs_btree_node_move_left(node, right, n, ncblk, ncblk); - - if (!buffer_dirty(path[level].bp_bh)) - mark_buffer_dirty(path[level].bp_bh); - if (!buffer_dirty(path[level].bp_sib_bh)) - mark_buffer_dirty(path[level].bp_sib_bh); - - path[level + 1].bp_index++; - nilfs_btree_promote_key(btree, path, level + 1, - nilfs_btree_node_get_key(right, 0)); - path[level + 1].bp_index--; - - brelse(path[level].bp_sib_bh); - path[level].bp_sib_bh = NULL; -} - -static void nilfs_btree_concat_left(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, __u64 *keyp, __u64 *ptrp) -{ - struct nilfs_btree_node *node, *left; - int n, ncblk; - - nilfs_btree_do_delete(btree, path, level, keyp, ptrp); - - node = nilfs_btree_get_nonroot_node(path, level); - left = nilfs_btree_get_sib_node(path, level); - ncblk = nilfs_btree_nchildren_per_block(btree); - - n = nilfs_btree_node_get_nchildren(node); - - nilfs_btree_node_move_left(left, node, n, ncblk, ncblk); - - if (!buffer_dirty(path[level].bp_sib_bh)) - mark_buffer_dirty(path[level].bp_sib_bh); - - nilfs_btnode_delete(path[level].bp_bh); - path[level].bp_bh = path[level].bp_sib_bh; - path[level].bp_sib_bh = NULL; - path[level].bp_index += nilfs_btree_node_get_nchildren(left); -} - -static void nilfs_btree_concat_right(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, __u64 *keyp, __u64 *ptrp) -{ - struct nilfs_btree_node *node, *right; - int n, ncblk; - - nilfs_btree_do_delete(btree, path, level, keyp, ptrp); - - node = nilfs_btree_get_nonroot_node(path, level); - right = nilfs_btree_get_sib_node(path, level); - ncblk = nilfs_btree_nchildren_per_block(btree); - - n = nilfs_btree_node_get_nchildren(right); - - nilfs_btree_node_move_left(node, right, n, ncblk, ncblk); - - if (!buffer_dirty(path[level].bp_bh)) - mark_buffer_dirty(path[level].bp_bh); - - nilfs_btnode_delete(path[level].bp_sib_bh); - path[level].bp_sib_bh = NULL; - path[level + 1].bp_index++; -} - -static void nilfs_btree_shrink(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, __u64 *keyp, __u64 *ptrp) -{ - struct nilfs_btree_node *root, *child; - int n, ncblk; - - nilfs_btree_do_delete(btree, path, level, keyp, ptrp); - - root = nilfs_btree_get_root(btree); - child = nilfs_btree_get_nonroot_node(path, level); - ncblk = nilfs_btree_nchildren_per_block(btree); - - nilfs_btree_node_delete(root, 0, NULL, NULL, - NILFS_BTREE_ROOT_NCHILDREN_MAX); - nilfs_btree_node_set_level(root, level); - n = nilfs_btree_node_get_nchildren(child); - nilfs_btree_node_move_left(root, child, n, - NILFS_BTREE_ROOT_NCHILDREN_MAX, ncblk); - - nilfs_btnode_delete(path[level].bp_bh); - path[level].bp_bh = NULL; -} - -static void nilfs_btree_nop(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, __u64 *keyp, __u64 *ptrp) -{ -} - -static int nilfs_btree_prepare_delete(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int *levelp, - struct nilfs_bmap_stats *stats, - struct inode *dat) -{ - struct buffer_head *bh; - struct nilfs_btree_node *node, *parent, *sib; - __u64 sibptr; - int pindex, dindex, level, ncmin, ncmax, ncblk, ret; - - ret = 0; - stats->bs_nblocks = 0; - ncmin = NILFS_BTREE_NODE_NCHILDREN_MIN(nilfs_btree_node_size(btree)); - ncblk = nilfs_btree_nchildren_per_block(btree); - - for (level = NILFS_BTREE_LEVEL_NODE_MIN, dindex = path[level].bp_index; - level < nilfs_btree_height(btree) - 1; - level++) { - node = nilfs_btree_get_nonroot_node(path, level); - path[level].bp_oldreq.bpr_ptr = - nilfs_btree_node_get_ptr(node, dindex, ncblk); - ret = nilfs_bmap_prepare_end_ptr(btree, - &path[level].bp_oldreq, dat); - if (ret < 0) - goto err_out_child_node; - - if (nilfs_btree_node_get_nchildren(node) > ncmin) { - path[level].bp_op = nilfs_btree_do_delete; - stats->bs_nblocks++; - goto out; - } - - parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax); - pindex = path[level + 1].bp_index; - dindex = pindex; - - if (pindex > 0) { - /* left sibling */ - sibptr = nilfs_btree_node_get_ptr(parent, pindex - 1, - ncmax); - ret = nilfs_btree_get_block(btree, sibptr, &bh); - if (ret < 0) - goto err_out_curr_node; - sib = (struct nilfs_btree_node *)bh->b_data; - if (nilfs_btree_node_get_nchildren(sib) > ncmin) { - path[level].bp_sib_bh = bh; - path[level].bp_op = nilfs_btree_borrow_left; - stats->bs_nblocks++; - goto out; - } else { - path[level].bp_sib_bh = bh; - path[level].bp_op = nilfs_btree_concat_left; - stats->bs_nblocks++; - /* continue; */ - } - } else if (pindex < - nilfs_btree_node_get_nchildren(parent) - 1) { - /* right sibling */ - sibptr = nilfs_btree_node_get_ptr(parent, pindex + 1, - ncmax); - ret = nilfs_btree_get_block(btree, sibptr, &bh); - if (ret < 0) - goto err_out_curr_node; - sib = (struct nilfs_btree_node *)bh->b_data; - if (nilfs_btree_node_get_nchildren(sib) > ncmin) { - path[level].bp_sib_bh = bh; - path[level].bp_op = nilfs_btree_borrow_right; - stats->bs_nblocks++; - goto out; - } else { - path[level].bp_sib_bh = bh; - path[level].bp_op = nilfs_btree_concat_right; - stats->bs_nblocks++; - /* - * When merging right sibling node - * into the current node, pointer to - * the right sibling node must be - * terminated instead. The adjustment - * below is required for that. - */ - dindex = pindex + 1; - /* continue; */ - } - } else { - /* no siblings */ - /* the only child of the root node */ - WARN_ON(level != nilfs_btree_height(btree) - 2); - if (nilfs_btree_node_get_nchildren(node) - 1 <= - NILFS_BTREE_ROOT_NCHILDREN_MAX) { - path[level].bp_op = nilfs_btree_shrink; - stats->bs_nblocks += 2; - level++; - path[level].bp_op = nilfs_btree_nop; - goto shrink_root_child; - } else { - path[level].bp_op = nilfs_btree_do_delete; - stats->bs_nblocks++; - goto out; - } - } - } - - /* child of the root node is deleted */ - path[level].bp_op = nilfs_btree_do_delete; - stats->bs_nblocks++; - -shrink_root_child: - node = nilfs_btree_get_root(btree); - path[level].bp_oldreq.bpr_ptr = - nilfs_btree_node_get_ptr(node, dindex, - NILFS_BTREE_ROOT_NCHILDREN_MAX); - - ret = nilfs_bmap_prepare_end_ptr(btree, &path[level].bp_oldreq, dat); - if (ret < 0) - goto err_out_child_node; - - /* success */ - out: - *levelp = level; - return ret; - - /* error */ - err_out_curr_node: - nilfs_bmap_abort_end_ptr(btree, &path[level].bp_oldreq, dat); - err_out_child_node: - for (level--; level >= NILFS_BTREE_LEVEL_NODE_MIN; level--) { - brelse(path[level].bp_sib_bh); - nilfs_bmap_abort_end_ptr(btree, &path[level].bp_oldreq, dat); - } - *levelp = level; - stats->bs_nblocks = 0; - return ret; -} - -static void nilfs_btree_commit_delete(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int maxlevel, struct inode *dat) -{ - int level; - - for (level = NILFS_BTREE_LEVEL_NODE_MIN; level <= maxlevel; level++) { - nilfs_bmap_commit_end_ptr(btree, &path[level].bp_oldreq, dat); - path[level].bp_op(btree, path, level, NULL, NULL); - } - - if (!nilfs_bmap_dirty(btree)) - nilfs_bmap_set_dirty(btree); -} - -static int nilfs_btree_delete(struct nilfs_bmap *btree, __u64 key) - -{ - struct nilfs_btree_path *path; - struct nilfs_bmap_stats stats; - struct inode *dat; - int level, ret; - - path = nilfs_btree_alloc_path(); - if (path == NULL) - return -ENOMEM; - - ret = nilfs_btree_do_lookup(btree, path, key, NULL, - NILFS_BTREE_LEVEL_NODE_MIN, 0); - if (ret < 0) - goto out; - - - dat = NILFS_BMAP_USE_VBN(btree) ? nilfs_bmap_get_dat(btree) : NULL; - - ret = nilfs_btree_prepare_delete(btree, path, &level, &stats, dat); - if (ret < 0) - goto out; - nilfs_btree_commit_delete(btree, path, level, dat); - nilfs_inode_sub_blocks(btree->b_inode, stats.bs_nblocks); - -out: - nilfs_btree_free_path(path); - return ret; -} - -static int nilfs_btree_last_key(const struct nilfs_bmap *btree, __u64 *keyp) -{ - struct nilfs_btree_path *path; - int ret; - - path = nilfs_btree_alloc_path(); - if (path == NULL) - return -ENOMEM; - - ret = nilfs_btree_do_lookup_last(btree, path, keyp, NULL); - - nilfs_btree_free_path(path); - - return ret; -} - -static int nilfs_btree_check_delete(struct nilfs_bmap *btree, __u64 key) -{ - struct buffer_head *bh; - struct nilfs_btree_node *root, *node; - __u64 maxkey, nextmaxkey; - __u64 ptr; - int nchildren, ret; - - root = nilfs_btree_get_root(btree); - switch (nilfs_btree_height(btree)) { - case 2: - bh = NULL; - node = root; - break; - case 3: - nchildren = nilfs_btree_node_get_nchildren(root); - if (nchildren > 1) - return 0; - ptr = nilfs_btree_node_get_ptr(root, nchildren - 1, - NILFS_BTREE_ROOT_NCHILDREN_MAX); - ret = nilfs_btree_get_block(btree, ptr, &bh); - if (ret < 0) - return ret; - node = (struct nilfs_btree_node *)bh->b_data; - break; - default: - return 0; - } - - nchildren = nilfs_btree_node_get_nchildren(node); - maxkey = nilfs_btree_node_get_key(node, nchildren - 1); - nextmaxkey = (nchildren > 1) ? - nilfs_btree_node_get_key(node, nchildren - 2) : 0; - if (bh != NULL) - brelse(bh); - - return (maxkey == key) && (nextmaxkey < NILFS_BMAP_LARGE_LOW); -} - -static int nilfs_btree_gather_data(struct nilfs_bmap *btree, - __u64 *keys, __u64 *ptrs, int nitems) -{ - struct buffer_head *bh; - struct nilfs_btree_node *node, *root; - __le64 *dkeys; - __le64 *dptrs; - __u64 ptr; - int nchildren, ncmax, i, ret; - - root = nilfs_btree_get_root(btree); - switch (nilfs_btree_height(btree)) { - case 2: - bh = NULL; - node = root; - ncmax = NILFS_BTREE_ROOT_NCHILDREN_MAX; - break; - case 3: - nchildren = nilfs_btree_node_get_nchildren(root); - WARN_ON(nchildren > 1); - ptr = nilfs_btree_node_get_ptr(root, nchildren - 1, - NILFS_BTREE_ROOT_NCHILDREN_MAX); - ret = nilfs_btree_get_block(btree, ptr, &bh); - if (ret < 0) - return ret; - node = (struct nilfs_btree_node *)bh->b_data; - ncmax = nilfs_btree_nchildren_per_block(btree); - break; - default: - node = NULL; - return -EINVAL; - } - - nchildren = nilfs_btree_node_get_nchildren(node); - if (nchildren < nitems) - nitems = nchildren; - dkeys = nilfs_btree_node_dkeys(node); - dptrs = nilfs_btree_node_dptrs(node, ncmax); - for (i = 0; i < nitems; i++) { - keys[i] = le64_to_cpu(dkeys[i]); - ptrs[i] = le64_to_cpu(dptrs[i]); - } - - if (bh != NULL) - brelse(bh); - - return nitems; -} - -static int -nilfs_btree_prepare_convert_and_insert(struct nilfs_bmap *btree, __u64 key, - union nilfs_bmap_ptr_req *dreq, - union nilfs_bmap_ptr_req *nreq, - struct buffer_head **bhp, - struct nilfs_bmap_stats *stats) -{ - struct buffer_head *bh; - struct inode *dat = NULL; - int ret; - - stats->bs_nblocks = 0; - - /* for data */ - /* cannot find near ptr */ - if (NILFS_BMAP_USE_VBN(btree)) { - dreq->bpr_ptr = nilfs_btree_find_target_v(btree, NULL, key); - dat = nilfs_bmap_get_dat(btree); - } - - ret = nilfs_bmap_prepare_alloc_ptr(btree, dreq, dat); - if (ret < 0) - return ret; - - *bhp = NULL; - stats->bs_nblocks++; - if (nreq != NULL) { - nreq->bpr_ptr = dreq->bpr_ptr + 1; - ret = nilfs_bmap_prepare_alloc_ptr(btree, nreq, dat); - if (ret < 0) - goto err_out_dreq; - - ret = nilfs_btree_get_new_block(btree, nreq->bpr_ptr, &bh); - if (ret < 0) - goto err_out_nreq; - - *bhp = bh; - stats->bs_nblocks++; - } - - /* success */ - return 0; - - /* error */ - err_out_nreq: - nilfs_bmap_abort_alloc_ptr(btree, nreq, dat); - err_out_dreq: - nilfs_bmap_abort_alloc_ptr(btree, dreq, dat); - stats->bs_nblocks = 0; - return ret; - -} - -static void -nilfs_btree_commit_convert_and_insert(struct nilfs_bmap *btree, - __u64 key, __u64 ptr, - const __u64 *keys, const __u64 *ptrs, - int n, - union nilfs_bmap_ptr_req *dreq, - union nilfs_bmap_ptr_req *nreq, - struct buffer_head *bh) -{ - struct nilfs_btree_node *node; - struct inode *dat; - __u64 tmpptr; - int ncblk; - - /* free resources */ - if (btree->b_ops->bop_clear != NULL) - btree->b_ops->bop_clear(btree); - - /* ptr must be a pointer to a buffer head. */ - set_buffer_nilfs_volatile((struct buffer_head *)((unsigned long)ptr)); - - /* convert and insert */ - dat = NILFS_BMAP_USE_VBN(btree) ? nilfs_bmap_get_dat(btree) : NULL; - nilfs_btree_init(btree); - if (nreq != NULL) { - nilfs_bmap_commit_alloc_ptr(btree, dreq, dat); - nilfs_bmap_commit_alloc_ptr(btree, nreq, dat); - - /* create child node at level 1 */ - node = (struct nilfs_btree_node *)bh->b_data; - ncblk = nilfs_btree_nchildren_per_block(btree); - nilfs_btree_node_init(node, 0, 1, n, ncblk, keys, ptrs); - nilfs_btree_node_insert(node, n, key, dreq->bpr_ptr, ncblk); - if (!buffer_dirty(bh)) - mark_buffer_dirty(bh); - if (!nilfs_bmap_dirty(btree)) - nilfs_bmap_set_dirty(btree); - - brelse(bh); - - /* create root node at level 2 */ - node = nilfs_btree_get_root(btree); - tmpptr = nreq->bpr_ptr; - nilfs_btree_node_init(node, NILFS_BTREE_NODE_ROOT, 2, 1, - NILFS_BTREE_ROOT_NCHILDREN_MAX, - &keys[0], &tmpptr); - } else { - nilfs_bmap_commit_alloc_ptr(btree, dreq, dat); - - /* create root node at level 1 */ - node = nilfs_btree_get_root(btree); - nilfs_btree_node_init(node, NILFS_BTREE_NODE_ROOT, 1, n, - NILFS_BTREE_ROOT_NCHILDREN_MAX, - keys, ptrs); - nilfs_btree_node_insert(node, n, key, dreq->bpr_ptr, - NILFS_BTREE_ROOT_NCHILDREN_MAX); - if (!nilfs_bmap_dirty(btree)) - nilfs_bmap_set_dirty(btree); - } - - if (NILFS_BMAP_USE_VBN(btree)) - nilfs_bmap_set_target_v(btree, key, dreq->bpr_ptr); -} - -/** - * nilfs_btree_convert_and_insert - - * @bmap: - * @key: - * @ptr: - * @keys: - * @ptrs: - * @n: - */ -int nilfs_btree_convert_and_insert(struct nilfs_bmap *btree, - __u64 key, __u64 ptr, - const __u64 *keys, const __u64 *ptrs, int n) -{ - struct buffer_head *bh; - union nilfs_bmap_ptr_req dreq, nreq, *di, *ni; - struct nilfs_bmap_stats stats; - int ret; - - if (n + 1 <= NILFS_BTREE_ROOT_NCHILDREN_MAX) { - di = &dreq; - ni = NULL; - } else if ((n + 1) <= NILFS_BTREE_NODE_NCHILDREN_MAX( - 1 << btree->b_inode->i_blkbits)) { - di = &dreq; - ni = &nreq; - } else { - di = NULL; - ni = NULL; - BUG(); - } - - ret = nilfs_btree_prepare_convert_and_insert(btree, key, di, ni, &bh, - &stats); - if (ret < 0) - return ret; - nilfs_btree_commit_convert_and_insert(btree, key, ptr, keys, ptrs, n, - di, ni, bh); - nilfs_inode_add_blocks(btree->b_inode, stats.bs_nblocks); - return 0; -} - -static int nilfs_btree_propagate_p(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, - struct buffer_head *bh) -{ - while ((++level < nilfs_btree_height(btree) - 1) && - !buffer_dirty(path[level].bp_bh)) - mark_buffer_dirty(path[level].bp_bh); - - return 0; -} - -static int nilfs_btree_prepare_update_v(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, struct inode *dat) -{ - struct nilfs_btree_node *parent; - int ncmax, ret; - - parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax); - path[level].bp_oldreq.bpr_ptr = - nilfs_btree_node_get_ptr(parent, path[level + 1].bp_index, - ncmax); - path[level].bp_newreq.bpr_ptr = path[level].bp_oldreq.bpr_ptr + 1; - ret = nilfs_dat_prepare_update(dat, &path[level].bp_oldreq.bpr_req, - &path[level].bp_newreq.bpr_req); - if (ret < 0) - return ret; - - if (buffer_nilfs_node(path[level].bp_bh)) { - path[level].bp_ctxt.oldkey = path[level].bp_oldreq.bpr_ptr; - path[level].bp_ctxt.newkey = path[level].bp_newreq.bpr_ptr; - path[level].bp_ctxt.bh = path[level].bp_bh; - ret = nilfs_btnode_prepare_change_key( - &NILFS_BMAP_I(btree)->i_btnode_cache, - &path[level].bp_ctxt); - if (ret < 0) { - nilfs_dat_abort_update(dat, - &path[level].bp_oldreq.bpr_req, - &path[level].bp_newreq.bpr_req); - return ret; - } - } - - return 0; -} - -static void nilfs_btree_commit_update_v(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, struct inode *dat) -{ - struct nilfs_btree_node *parent; - int ncmax; - - nilfs_dat_commit_update(dat, &path[level].bp_oldreq.bpr_req, - &path[level].bp_newreq.bpr_req, - btree->b_ptr_type == NILFS_BMAP_PTR_VS); - - if (buffer_nilfs_node(path[level].bp_bh)) { - nilfs_btnode_commit_change_key( - &NILFS_BMAP_I(btree)->i_btnode_cache, - &path[level].bp_ctxt); - path[level].bp_bh = path[level].bp_ctxt.bh; - } - set_buffer_nilfs_volatile(path[level].bp_bh); - - parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax); - nilfs_btree_node_set_ptr(parent, path[level + 1].bp_index, - path[level].bp_newreq.bpr_ptr, ncmax); -} - -static void nilfs_btree_abort_update_v(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, struct inode *dat) -{ - nilfs_dat_abort_update(dat, &path[level].bp_oldreq.bpr_req, - &path[level].bp_newreq.bpr_req); - if (buffer_nilfs_node(path[level].bp_bh)) - nilfs_btnode_abort_change_key( - &NILFS_BMAP_I(btree)->i_btnode_cache, - &path[level].bp_ctxt); -} - -static int nilfs_btree_prepare_propagate_v(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int minlevel, int *maxlevelp, - struct inode *dat) -{ - int level, ret; - - level = minlevel; - if (!buffer_nilfs_volatile(path[level].bp_bh)) { - ret = nilfs_btree_prepare_update_v(btree, path, level, dat); - if (ret < 0) - return ret; - } - while ((++level < nilfs_btree_height(btree) - 1) && - !buffer_dirty(path[level].bp_bh)) { - - WARN_ON(buffer_nilfs_volatile(path[level].bp_bh)); - ret = nilfs_btree_prepare_update_v(btree, path, level, dat); - if (ret < 0) - goto out; - } - - /* success */ - *maxlevelp = level - 1; - return 0; - - /* error */ - out: - while (--level > minlevel) - nilfs_btree_abort_update_v(btree, path, level, dat); - if (!buffer_nilfs_volatile(path[level].bp_bh)) - nilfs_btree_abort_update_v(btree, path, level, dat); - return ret; -} - -static void nilfs_btree_commit_propagate_v(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int minlevel, int maxlevel, - struct buffer_head *bh, - struct inode *dat) -{ - int level; - - if (!buffer_nilfs_volatile(path[minlevel].bp_bh)) - nilfs_btree_commit_update_v(btree, path, minlevel, dat); - - for (level = minlevel + 1; level <= maxlevel; level++) - nilfs_btree_commit_update_v(btree, path, level, dat); -} - -static int nilfs_btree_propagate_v(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, struct buffer_head *bh) -{ - int maxlevel = 0, ret; - struct nilfs_btree_node *parent; - struct inode *dat = nilfs_bmap_get_dat(btree); - __u64 ptr; - int ncmax; - - get_bh(bh); - path[level].bp_bh = bh; - ret = nilfs_btree_prepare_propagate_v(btree, path, level, &maxlevel, - dat); - if (ret < 0) - goto out; - - if (buffer_nilfs_volatile(path[level].bp_bh)) { - parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax); - ptr = nilfs_btree_node_get_ptr(parent, - path[level + 1].bp_index, - ncmax); - ret = nilfs_dat_mark_dirty(dat, ptr); - if (ret < 0) - goto out; - } - - nilfs_btree_commit_propagate_v(btree, path, level, maxlevel, bh, dat); - - out: - brelse(path[level].bp_bh); - path[level].bp_bh = NULL; - return ret; -} - -static int nilfs_btree_propagate(struct nilfs_bmap *btree, - struct buffer_head *bh) -{ - struct nilfs_btree_path *path; - struct nilfs_btree_node *node; - __u64 key; - int level, ret; - - WARN_ON(!buffer_dirty(bh)); - - path = nilfs_btree_alloc_path(); - if (path == NULL) - return -ENOMEM; - - if (buffer_nilfs_node(bh)) { - node = (struct nilfs_btree_node *)bh->b_data; - key = nilfs_btree_node_get_key(node, 0); - level = nilfs_btree_node_get_level(node); - } else { - key = nilfs_bmap_data_get_key(btree, bh); - level = NILFS_BTREE_LEVEL_DATA; - } - - ret = nilfs_btree_do_lookup(btree, path, key, NULL, level + 1, 0); - if (ret < 0) { - if (unlikely(ret == -ENOENT)) - printk(KERN_CRIT "%s: key = %llu, level == %d\n", - __func__, (unsigned long long)key, level); - goto out; - } - - ret = NILFS_BMAP_USE_VBN(btree) ? - nilfs_btree_propagate_v(btree, path, level, bh) : - nilfs_btree_propagate_p(btree, path, level, bh); - - out: - nilfs_btree_free_path(path); - - return ret; -} - -static int nilfs_btree_propagate_gc(struct nilfs_bmap *btree, - struct buffer_head *bh) -{ - return nilfs_dat_mark_dirty(nilfs_bmap_get_dat(btree), bh->b_blocknr); -} - -static void nilfs_btree_add_dirty_buffer(struct nilfs_bmap *btree, - struct list_head *lists, - struct buffer_head *bh) -{ - struct list_head *head; - struct buffer_head *cbh; - struct nilfs_btree_node *node, *cnode; - __u64 key, ckey; - int level; - - get_bh(bh); - node = (struct nilfs_btree_node *)bh->b_data; - key = nilfs_btree_node_get_key(node, 0); - level = nilfs_btree_node_get_level(node); - if (level < NILFS_BTREE_LEVEL_NODE_MIN || - level >= NILFS_BTREE_LEVEL_MAX) { - dump_stack(); - printk(KERN_WARNING - "%s: invalid btree level: %d (key=%llu, ino=%lu, " - "blocknr=%llu)\n", - __func__, level, (unsigned long long)key, - NILFS_BMAP_I(btree)->vfs_inode.i_ino, - (unsigned long long)bh->b_blocknr); - return; - } - - list_for_each(head, &lists[level]) { - cbh = list_entry(head, struct buffer_head, b_assoc_buffers); - cnode = (struct nilfs_btree_node *)cbh->b_data; - ckey = nilfs_btree_node_get_key(cnode, 0); - if (key < ckey) - break; - } - list_add_tail(&bh->b_assoc_buffers, head); -} - -static void nilfs_btree_lookup_dirty_buffers(struct nilfs_bmap *btree, - struct list_head *listp) -{ - struct address_space *btcache = &NILFS_BMAP_I(btree)->i_btnode_cache; - struct list_head lists[NILFS_BTREE_LEVEL_MAX]; - struct pagevec pvec; - struct buffer_head *bh, *head; - pgoff_t index = 0; - int level, i; - - for (level = NILFS_BTREE_LEVEL_NODE_MIN; - level < NILFS_BTREE_LEVEL_MAX; - level++) - INIT_LIST_HEAD(&lists[level]); - - pagevec_init(&pvec, 0); - - while (pagevec_lookup_tag(&pvec, btcache, &index, PAGECACHE_TAG_DIRTY, - PAGEVEC_SIZE)) { - for (i = 0; i < pagevec_count(&pvec); i++) { - bh = head = page_buffers(pvec.pages[i]); - do { - if (buffer_dirty(bh)) - nilfs_btree_add_dirty_buffer(btree, - lists, bh); - } while ((bh = bh->b_this_page) != head); - } - pagevec_release(&pvec); - cond_resched(); - } - - for (level = NILFS_BTREE_LEVEL_NODE_MIN; - level < NILFS_BTREE_LEVEL_MAX; - level++) - list_splice_tail(&lists[level], listp); -} - -static int nilfs_btree_assign_p(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, - struct buffer_head **bh, - sector_t blocknr, - union nilfs_binfo *binfo) -{ - struct nilfs_btree_node *parent; - __u64 key; - __u64 ptr; - int ncmax, ret; - - parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax); - ptr = nilfs_btree_node_get_ptr(parent, path[level + 1].bp_index, - ncmax); - if (buffer_nilfs_node(*bh)) { - path[level].bp_ctxt.oldkey = ptr; - path[level].bp_ctxt.newkey = blocknr; - path[level].bp_ctxt.bh = *bh; - ret = nilfs_btnode_prepare_change_key( - &NILFS_BMAP_I(btree)->i_btnode_cache, - &path[level].bp_ctxt); - if (ret < 0) - return ret; - nilfs_btnode_commit_change_key( - &NILFS_BMAP_I(btree)->i_btnode_cache, - &path[level].bp_ctxt); - *bh = path[level].bp_ctxt.bh; - } - - nilfs_btree_node_set_ptr(parent, path[level + 1].bp_index, blocknr, - ncmax); - - key = nilfs_btree_node_get_key(parent, path[level + 1].bp_index); - /* on-disk format */ - binfo->bi_dat.bi_blkoff = cpu_to_le64(key); - binfo->bi_dat.bi_level = level; - - return 0; -} - -static int nilfs_btree_assign_v(struct nilfs_bmap *btree, - struct nilfs_btree_path *path, - int level, - struct buffer_head **bh, - sector_t blocknr, - union nilfs_binfo *binfo) -{ - struct nilfs_btree_node *parent; - struct inode *dat = nilfs_bmap_get_dat(btree); - __u64 key; - __u64 ptr; - union nilfs_bmap_ptr_req req; - int ncmax, ret; - - parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax); - ptr = nilfs_btree_node_get_ptr(parent, path[level + 1].bp_index, - ncmax); - req.bpr_ptr = ptr; - ret = nilfs_dat_prepare_start(dat, &req.bpr_req); - if (ret < 0) - return ret; - nilfs_dat_commit_start(dat, &req.bpr_req, blocknr); - - key = nilfs_btree_node_get_key(parent, path[level + 1].bp_index); - /* on-disk format */ - binfo->bi_v.bi_vblocknr = cpu_to_le64(ptr); - binfo->bi_v.bi_blkoff = cpu_to_le64(key); - - return 0; -} - -static int nilfs_btree_assign(struct nilfs_bmap *btree, - struct buffer_head **bh, - sector_t blocknr, - union nilfs_binfo *binfo) -{ - struct nilfs_btree_path *path; - struct nilfs_btree_node *node; - __u64 key; - int level, ret; - - path = nilfs_btree_alloc_path(); - if (path == NULL) - return -ENOMEM; - - if (buffer_nilfs_node(*bh)) { - node = (struct nilfs_btree_node *)(*bh)->b_data; - key = nilfs_btree_node_get_key(node, 0); - level = nilfs_btree_node_get_level(node); - } else { - key = nilfs_bmap_data_get_key(btree, *bh); - level = NILFS_BTREE_LEVEL_DATA; - } - - ret = nilfs_btree_do_lookup(btree, path, key, NULL, level + 1, 0); - if (ret < 0) { - WARN_ON(ret == -ENOENT); - goto out; - } - - ret = NILFS_BMAP_USE_VBN(btree) ? - nilfs_btree_assign_v(btree, path, level, bh, blocknr, binfo) : - nilfs_btree_assign_p(btree, path, level, bh, blocknr, binfo); - - out: - nilfs_btree_free_path(path); - - return ret; -} - -static int nilfs_btree_assign_gc(struct nilfs_bmap *btree, - struct buffer_head **bh, - sector_t blocknr, - union nilfs_binfo *binfo) -{ - struct nilfs_btree_node *node; - __u64 key; - int ret; - - ret = nilfs_dat_move(nilfs_bmap_get_dat(btree), (*bh)->b_blocknr, - blocknr); - if (ret < 0) - return ret; - - if (buffer_nilfs_node(*bh)) { - node = (struct nilfs_btree_node *)(*bh)->b_data; - key = nilfs_btree_node_get_key(node, 0); - } else - key = nilfs_bmap_data_get_key(btree, *bh); - - /* on-disk format */ - binfo->bi_v.bi_vblocknr = cpu_to_le64((*bh)->b_blocknr); - binfo->bi_v.bi_blkoff = cpu_to_le64(key); - - return 0; -} - -static int nilfs_btree_mark(struct nilfs_bmap *btree, __u64 key, int level) -{ - struct buffer_head *bh; - struct nilfs_btree_path *path; - __u64 ptr; - int ret; - - path = nilfs_btree_alloc_path(); - if (path == NULL) - return -ENOMEM; - - ret = nilfs_btree_do_lookup(btree, path, key, &ptr, level + 1, 0); - if (ret < 0) { - WARN_ON(ret == -ENOENT); - goto out; - } - ret = nilfs_btree_get_block(btree, ptr, &bh); - if (ret < 0) { - WARN_ON(ret == -ENOENT); - goto out; - } - - if (!buffer_dirty(bh)) - mark_buffer_dirty(bh); - brelse(bh); - if (!nilfs_bmap_dirty(btree)) - nilfs_bmap_set_dirty(btree); - - out: - nilfs_btree_free_path(path); - return ret; -} - -static const struct nilfs_bmap_operations nilfs_btree_ops = { - .bop_lookup = nilfs_btree_lookup, - .bop_lookup_contig = nilfs_btree_lookup_contig, - .bop_insert = nilfs_btree_insert, - .bop_delete = nilfs_btree_delete, - .bop_clear = NULL, - - .bop_propagate = nilfs_btree_propagate, - - .bop_lookup_dirty_buffers = nilfs_btree_lookup_dirty_buffers, - - .bop_assign = nilfs_btree_assign, - .bop_mark = nilfs_btree_mark, - - .bop_last_key = nilfs_btree_last_key, - .bop_check_insert = NULL, - .bop_check_delete = nilfs_btree_check_delete, - .bop_gather_data = nilfs_btree_gather_data, -}; - -static const struct nilfs_bmap_operations nilfs_btree_ops_gc = { - .bop_lookup = NULL, - .bop_lookup_contig = NULL, - .bop_insert = NULL, - .bop_delete = NULL, - .bop_clear = NULL, - - .bop_propagate = nilfs_btree_propagate_gc, - - .bop_lookup_dirty_buffers = nilfs_btree_lookup_dirty_buffers, - - .bop_assign = nilfs_btree_assign_gc, - .bop_mark = NULL, - - .bop_last_key = NULL, - .bop_check_insert = NULL, - .bop_check_delete = NULL, - .bop_gather_data = NULL, -}; - -int nilfs_btree_init(struct nilfs_bmap *bmap) -{ - bmap->b_ops = &nilfs_btree_ops; - bmap->b_nchildren_per_block = - NILFS_BTREE_NODE_NCHILDREN_MAX(nilfs_btree_node_size(bmap)); - return 0; -} - -void nilfs_btree_init_gc(struct nilfs_bmap *bmap) -{ - bmap->b_ops = &nilfs_btree_ops_gc; - bmap->b_nchildren_per_block = - NILFS_BTREE_NODE_NCHILDREN_MAX(nilfs_btree_node_size(bmap)); -} diff --git a/ANDROID_3.4.5/fs/nilfs2/btree.h b/ANDROID_3.4.5/fs/nilfs2/btree.h deleted file mode 100644 index 22c02e35..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/btree.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * btree.h - NILFS B-tree. - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Koji Sato <koji@osrg.net>. - */ - -#ifndef _NILFS_BTREE_H -#define _NILFS_BTREE_H - -#include <linux/types.h> -#include <linux/buffer_head.h> -#include <linux/list.h> -#include <linux/nilfs2_fs.h> -#include "btnode.h" -#include "bmap.h" - -/** - * struct nilfs_btree_path - A path on which B-tree operations are executed - * @bp_bh: buffer head of node block - * @bp_sib_bh: buffer head of sibling node block - * @bp_index: index of child node - * @bp_oldreq: ptr end request for old ptr - * @bp_newreq: ptr alloc request for new ptr - * @bp_op: rebalance operation - */ -struct nilfs_btree_path { - struct buffer_head *bp_bh; - struct buffer_head *bp_sib_bh; - int bp_index; - union nilfs_bmap_ptr_req bp_oldreq; - union nilfs_bmap_ptr_req bp_newreq; - struct nilfs_btnode_chkey_ctxt bp_ctxt; - void (*bp_op)(struct nilfs_bmap *, struct nilfs_btree_path *, - int, __u64 *, __u64 *); -}; - -#define NILFS_BTREE_ROOT_SIZE NILFS_BMAP_SIZE -#define NILFS_BTREE_ROOT_NCHILDREN_MAX \ - ((NILFS_BTREE_ROOT_SIZE - sizeof(struct nilfs_btree_node)) / \ - (sizeof(__le64 /* dkey */) + sizeof(__le64 /* dptr */))) -#define NILFS_BTREE_ROOT_NCHILDREN_MIN 0 -#define NILFS_BTREE_NODE_EXTRA_PAD_SIZE (sizeof(__le64)) -#define NILFS_BTREE_NODE_NCHILDREN_MAX(nodesize) \ - (((nodesize) - sizeof(struct nilfs_btree_node) - \ - NILFS_BTREE_NODE_EXTRA_PAD_SIZE) / \ - (sizeof(__le64 /* dkey */) + sizeof(__le64 /* dptr */))) -#define NILFS_BTREE_NODE_NCHILDREN_MIN(nodesize) \ - ((NILFS_BTREE_NODE_NCHILDREN_MAX(nodesize) - 1) / 2 + 1) -#define NILFS_BTREE_KEY_MIN ((__u64)0) -#define NILFS_BTREE_KEY_MAX (~(__u64)0) - -extern struct kmem_cache *nilfs_btree_path_cache; - -int nilfs_btree_init(struct nilfs_bmap *); -int nilfs_btree_convert_and_insert(struct nilfs_bmap *, __u64, __u64, - const __u64 *, const __u64 *, int); -void nilfs_btree_init_gc(struct nilfs_bmap *); - -int nilfs_btree_broken_node_block(struct buffer_head *bh); - -#endif /* _NILFS_BTREE_H */ diff --git a/ANDROID_3.4.5/fs/nilfs2/cpfile.c b/ANDROID_3.4.5/fs/nilfs2/cpfile.c deleted file mode 100644 index dab5c4c6..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/cpfile.c +++ /dev/null @@ -1,965 +0,0 @@ -/* - * cpfile.c - NILFS checkpoint file. - * - * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Koji Sato <koji@osrg.net>. - */ - -#include <linux/kernel.h> -#include <linux/fs.h> -#include <linux/string.h> -#include <linux/buffer_head.h> -#include <linux/errno.h> -#include <linux/nilfs2_fs.h> -#include "mdt.h" -#include "cpfile.h" - - -static inline unsigned long -nilfs_cpfile_checkpoints_per_block(const struct inode *cpfile) -{ - return NILFS_MDT(cpfile)->mi_entries_per_block; -} - -/* block number from the beginning of the file */ -static unsigned long -nilfs_cpfile_get_blkoff(const struct inode *cpfile, __u64 cno) -{ - __u64 tcno = cno + NILFS_MDT(cpfile)->mi_first_entry_offset - 1; - do_div(tcno, nilfs_cpfile_checkpoints_per_block(cpfile)); - return (unsigned long)tcno; -} - -/* offset in block */ -static unsigned long -nilfs_cpfile_get_offset(const struct inode *cpfile, __u64 cno) -{ - __u64 tcno = cno + NILFS_MDT(cpfile)->mi_first_entry_offset - 1; - return do_div(tcno, nilfs_cpfile_checkpoints_per_block(cpfile)); -} - -static unsigned long -nilfs_cpfile_checkpoints_in_block(const struct inode *cpfile, - __u64 curr, - __u64 max) -{ - return min_t(__u64, - nilfs_cpfile_checkpoints_per_block(cpfile) - - nilfs_cpfile_get_offset(cpfile, curr), - max - curr); -} - -static inline int nilfs_cpfile_is_in_first(const struct inode *cpfile, - __u64 cno) -{ - return nilfs_cpfile_get_blkoff(cpfile, cno) == 0; -} - -static unsigned int -nilfs_cpfile_block_add_valid_checkpoints(const struct inode *cpfile, - struct buffer_head *bh, - void *kaddr, - unsigned int n) -{ - struct nilfs_checkpoint *cp = kaddr + bh_offset(bh); - unsigned int count; - - count = le32_to_cpu(cp->cp_checkpoints_count) + n; - cp->cp_checkpoints_count = cpu_to_le32(count); - return count; -} - -static unsigned int -nilfs_cpfile_block_sub_valid_checkpoints(const struct inode *cpfile, - struct buffer_head *bh, - void *kaddr, - unsigned int n) -{ - struct nilfs_checkpoint *cp = kaddr + bh_offset(bh); - unsigned int count; - - WARN_ON(le32_to_cpu(cp->cp_checkpoints_count) < n); - count = le32_to_cpu(cp->cp_checkpoints_count) - n; - cp->cp_checkpoints_count = cpu_to_le32(count); - return count; -} - -static inline struct nilfs_cpfile_header * -nilfs_cpfile_block_get_header(const struct inode *cpfile, - struct buffer_head *bh, - void *kaddr) -{ - return kaddr + bh_offset(bh); -} - -static struct nilfs_checkpoint * -nilfs_cpfile_block_get_checkpoint(const struct inode *cpfile, __u64 cno, - struct buffer_head *bh, - void *kaddr) -{ - return kaddr + bh_offset(bh) + nilfs_cpfile_get_offset(cpfile, cno) * - NILFS_MDT(cpfile)->mi_entry_size; -} - -static void nilfs_cpfile_block_init(struct inode *cpfile, - struct buffer_head *bh, - void *kaddr) -{ - struct nilfs_checkpoint *cp = kaddr + bh_offset(bh); - size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size; - int n = nilfs_cpfile_checkpoints_per_block(cpfile); - - while (n-- > 0) { - nilfs_checkpoint_set_invalid(cp); - cp = (void *)cp + cpsz; - } -} - -static inline int nilfs_cpfile_get_header_block(struct inode *cpfile, - struct buffer_head **bhp) -{ - return nilfs_mdt_get_block(cpfile, 0, 0, NULL, bhp); -} - -static inline int nilfs_cpfile_get_checkpoint_block(struct inode *cpfile, - __u64 cno, - int create, - struct buffer_head **bhp) -{ - return nilfs_mdt_get_block(cpfile, - nilfs_cpfile_get_blkoff(cpfile, cno), - create, nilfs_cpfile_block_init, bhp); -} - -static inline int nilfs_cpfile_delete_checkpoint_block(struct inode *cpfile, - __u64 cno) -{ - return nilfs_mdt_delete_block(cpfile, - nilfs_cpfile_get_blkoff(cpfile, cno)); -} - -/** - * nilfs_cpfile_get_checkpoint - get a checkpoint - * @cpfile: inode of checkpoint file - * @cno: checkpoint number - * @create: create flag - * @cpp: pointer to a checkpoint - * @bhp: pointer to a buffer head - * - * Description: nilfs_cpfile_get_checkpoint() acquires the checkpoint - * specified by @cno. A new checkpoint will be created if @cno is the current - * checkpoint number and @create is nonzero. - * - * Return Value: On success, 0 is returned, and the checkpoint and the - * buffer head of the buffer on which the checkpoint is located are stored in - * the place pointed by @cpp and @bhp, respectively. On error, one of the - * following negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - * - * %-ENOENT - No such checkpoint. - * - * %-EINVAL - invalid checkpoint. - */ -int nilfs_cpfile_get_checkpoint(struct inode *cpfile, - __u64 cno, - int create, - struct nilfs_checkpoint **cpp, - struct buffer_head **bhp) -{ - struct buffer_head *header_bh, *cp_bh; - struct nilfs_cpfile_header *header; - struct nilfs_checkpoint *cp; - void *kaddr; - int ret; - - if (unlikely(cno < 1 || cno > nilfs_mdt_cno(cpfile) || - (cno < nilfs_mdt_cno(cpfile) && create))) - return -EINVAL; - - down_write(&NILFS_MDT(cpfile)->mi_sem); - - ret = nilfs_cpfile_get_header_block(cpfile, &header_bh); - if (ret < 0) - goto out_sem; - ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, create, &cp_bh); - if (ret < 0) - goto out_header; - kaddr = kmap(cp_bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr); - if (nilfs_checkpoint_invalid(cp)) { - if (!create) { - kunmap(cp_bh->b_page); - brelse(cp_bh); - ret = -ENOENT; - goto out_header; - } - /* a newly-created checkpoint */ - nilfs_checkpoint_clear_invalid(cp); - if (!nilfs_cpfile_is_in_first(cpfile, cno)) - nilfs_cpfile_block_add_valid_checkpoints(cpfile, cp_bh, - kaddr, 1); - mark_buffer_dirty(cp_bh); - - kaddr = kmap_atomic(header_bh->b_page); - header = nilfs_cpfile_block_get_header(cpfile, header_bh, - kaddr); - le64_add_cpu(&header->ch_ncheckpoints, 1); - kunmap_atomic(kaddr); - mark_buffer_dirty(header_bh); - nilfs_mdt_mark_dirty(cpfile); - } - - if (cpp != NULL) - *cpp = cp; - *bhp = cp_bh; - - out_header: - brelse(header_bh); - - out_sem: - up_write(&NILFS_MDT(cpfile)->mi_sem); - return ret; -} - -/** - * nilfs_cpfile_put_checkpoint - put a checkpoint - * @cpfile: inode of checkpoint file - * @cno: checkpoint number - * @bh: buffer head - * - * Description: nilfs_cpfile_put_checkpoint() releases the checkpoint - * specified by @cno. @bh must be the buffer head which has been returned by - * a previous call to nilfs_cpfile_get_checkpoint() with @cno. - */ -void nilfs_cpfile_put_checkpoint(struct inode *cpfile, __u64 cno, - struct buffer_head *bh) -{ - kunmap(bh->b_page); - brelse(bh); -} - -/** - * nilfs_cpfile_delete_checkpoints - delete checkpoints - * @cpfile: inode of checkpoint file - * @start: start checkpoint number - * @end: end checkpoint numer - * - * Description: nilfs_cpfile_delete_checkpoints() deletes the checkpoints in - * the period from @start to @end, excluding @end itself. The checkpoints - * which have been already deleted are ignored. - * - * Return Value: On success, 0 is returned. On error, one of the following - * negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - * - * %-EINVAL - invalid checkpoints. - */ -int nilfs_cpfile_delete_checkpoints(struct inode *cpfile, - __u64 start, - __u64 end) -{ - struct buffer_head *header_bh, *cp_bh; - struct nilfs_cpfile_header *header; - struct nilfs_checkpoint *cp; - size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size; - __u64 cno; - void *kaddr; - unsigned long tnicps; - int ret, ncps, nicps, count, i; - - if (unlikely(start == 0 || start > end)) { - printk(KERN_ERR "%s: invalid range of checkpoint numbers: " - "[%llu, %llu)\n", __func__, - (unsigned long long)start, (unsigned long long)end); - return -EINVAL; - } - - down_write(&NILFS_MDT(cpfile)->mi_sem); - - ret = nilfs_cpfile_get_header_block(cpfile, &header_bh); - if (ret < 0) - goto out_sem; - tnicps = 0; - - for (cno = start; cno < end; cno += ncps) { - ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, end); - ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh); - if (ret < 0) { - if (ret != -ENOENT) - break; - /* skip hole */ - ret = 0; - continue; - } - - kaddr = kmap_atomic(cp_bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint( - cpfile, cno, cp_bh, kaddr); - nicps = 0; - for (i = 0; i < ncps; i++, cp = (void *)cp + cpsz) { - WARN_ON(nilfs_checkpoint_snapshot(cp)); - if (!nilfs_checkpoint_invalid(cp)) { - nilfs_checkpoint_set_invalid(cp); - nicps++; - } - } - if (nicps > 0) { - tnicps += nicps; - mark_buffer_dirty(cp_bh); - nilfs_mdt_mark_dirty(cpfile); - if (!nilfs_cpfile_is_in_first(cpfile, cno)) { - count = - nilfs_cpfile_block_sub_valid_checkpoints( - cpfile, cp_bh, kaddr, nicps); - if (count == 0) { - /* make hole */ - kunmap_atomic(kaddr); - brelse(cp_bh); - ret = - nilfs_cpfile_delete_checkpoint_block( - cpfile, cno); - if (ret == 0) - continue; - printk(KERN_ERR - "%s: cannot delete block\n", - __func__); - break; - } - } - } - - kunmap_atomic(kaddr); - brelse(cp_bh); - } - - if (tnicps > 0) { - kaddr = kmap_atomic(header_bh->b_page); - header = nilfs_cpfile_block_get_header(cpfile, header_bh, - kaddr); - le64_add_cpu(&header->ch_ncheckpoints, -(u64)tnicps); - mark_buffer_dirty(header_bh); - nilfs_mdt_mark_dirty(cpfile); - kunmap_atomic(kaddr); - } - - brelse(header_bh); - - out_sem: - up_write(&NILFS_MDT(cpfile)->mi_sem); - return ret; -} - -static void nilfs_cpfile_checkpoint_to_cpinfo(struct inode *cpfile, - struct nilfs_checkpoint *cp, - struct nilfs_cpinfo *ci) -{ - ci->ci_flags = le32_to_cpu(cp->cp_flags); - ci->ci_cno = le64_to_cpu(cp->cp_cno); - ci->ci_create = le64_to_cpu(cp->cp_create); - ci->ci_nblk_inc = le64_to_cpu(cp->cp_nblk_inc); - ci->ci_inodes_count = le64_to_cpu(cp->cp_inodes_count); - ci->ci_blocks_count = le64_to_cpu(cp->cp_blocks_count); - ci->ci_next = le64_to_cpu(cp->cp_snapshot_list.ssl_next); -} - -static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 *cnop, - void *buf, unsigned cisz, size_t nci) -{ - struct nilfs_checkpoint *cp; - struct nilfs_cpinfo *ci = buf; - struct buffer_head *bh; - size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size; - __u64 cur_cno = nilfs_mdt_cno(cpfile), cno = *cnop; - void *kaddr; - int n, ret; - int ncps, i; - - if (cno == 0) - return -ENOENT; /* checkpoint number 0 is invalid */ - down_read(&NILFS_MDT(cpfile)->mi_sem); - - for (n = 0; cno < cur_cno && n < nci; cno += ncps) { - ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, cur_cno); - ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &bh); - if (ret < 0) { - if (ret != -ENOENT) - goto out; - continue; /* skip hole */ - } - - kaddr = kmap_atomic(bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr); - for (i = 0; i < ncps && n < nci; i++, cp = (void *)cp + cpsz) { - if (!nilfs_checkpoint_invalid(cp)) { - nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp, - ci); - ci = (void *)ci + cisz; - n++; - } - } - kunmap_atomic(kaddr); - brelse(bh); - } - - ret = n; - if (n > 0) { - ci = (void *)ci - cisz; - *cnop = ci->ci_cno + 1; - } - - out: - up_read(&NILFS_MDT(cpfile)->mi_sem); - return ret; -} - -static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop, - void *buf, unsigned cisz, size_t nci) -{ - struct buffer_head *bh; - struct nilfs_cpfile_header *header; - struct nilfs_checkpoint *cp; - struct nilfs_cpinfo *ci = buf; - __u64 curr = *cnop, next; - unsigned long curr_blkoff, next_blkoff; - void *kaddr; - int n = 0, ret; - - down_read(&NILFS_MDT(cpfile)->mi_sem); - - if (curr == 0) { - ret = nilfs_cpfile_get_header_block(cpfile, &bh); - if (ret < 0) - goto out; - kaddr = kmap_atomic(bh->b_page); - header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr); - curr = le64_to_cpu(header->ch_snapshot_list.ssl_next); - kunmap_atomic(kaddr); - brelse(bh); - if (curr == 0) { - ret = 0; - goto out; - } - } else if (unlikely(curr == ~(__u64)0)) { - ret = 0; - goto out; - } - - curr_blkoff = nilfs_cpfile_get_blkoff(cpfile, curr); - ret = nilfs_cpfile_get_checkpoint_block(cpfile, curr, 0, &bh); - if (unlikely(ret < 0)) { - if (ret == -ENOENT) - ret = 0; /* No snapshots (started from a hole block) */ - goto out; - } - kaddr = kmap_atomic(bh->b_page); - while (n < nci) { - cp = nilfs_cpfile_block_get_checkpoint(cpfile, curr, bh, kaddr); - curr = ~(__u64)0; /* Terminator */ - if (unlikely(nilfs_checkpoint_invalid(cp) || - !nilfs_checkpoint_snapshot(cp))) - break; - nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp, ci); - ci = (void *)ci + cisz; - n++; - next = le64_to_cpu(cp->cp_snapshot_list.ssl_next); - if (next == 0) - break; /* reach end of the snapshot list */ - - next_blkoff = nilfs_cpfile_get_blkoff(cpfile, next); - if (curr_blkoff != next_blkoff) { - kunmap_atomic(kaddr); - brelse(bh); - ret = nilfs_cpfile_get_checkpoint_block(cpfile, next, - 0, &bh); - if (unlikely(ret < 0)) { - WARN_ON(ret == -ENOENT); - goto out; - } - kaddr = kmap_atomic(bh->b_page); - } - curr = next; - curr_blkoff = next_blkoff; - } - kunmap_atomic(kaddr); - brelse(bh); - *cnop = curr; - ret = n; - - out: - up_read(&NILFS_MDT(cpfile)->mi_sem); - return ret; -} - -/** - * nilfs_cpfile_get_cpinfo - - * @cpfile: - * @cno: - * @ci: - * @nci: - */ - -ssize_t nilfs_cpfile_get_cpinfo(struct inode *cpfile, __u64 *cnop, int mode, - void *buf, unsigned cisz, size_t nci) -{ - switch (mode) { - case NILFS_CHECKPOINT: - return nilfs_cpfile_do_get_cpinfo(cpfile, cnop, buf, cisz, nci); - case NILFS_SNAPSHOT: - return nilfs_cpfile_do_get_ssinfo(cpfile, cnop, buf, cisz, nci); - default: - return -EINVAL; - } -} - -/** - * nilfs_cpfile_delete_checkpoint - - * @cpfile: - * @cno: - */ -int nilfs_cpfile_delete_checkpoint(struct inode *cpfile, __u64 cno) -{ - struct nilfs_cpinfo ci; - __u64 tcno = cno; - ssize_t nci; - - nci = nilfs_cpfile_do_get_cpinfo(cpfile, &tcno, &ci, sizeof(ci), 1); - if (nci < 0) - return nci; - else if (nci == 0 || ci.ci_cno != cno) - return -ENOENT; - else if (nilfs_cpinfo_snapshot(&ci)) - return -EBUSY; - - return nilfs_cpfile_delete_checkpoints(cpfile, cno, cno + 1); -} - -static struct nilfs_snapshot_list * -nilfs_cpfile_block_get_snapshot_list(const struct inode *cpfile, - __u64 cno, - struct buffer_head *bh, - void *kaddr) -{ - struct nilfs_cpfile_header *header; - struct nilfs_checkpoint *cp; - struct nilfs_snapshot_list *list; - - if (cno != 0) { - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr); - list = &cp->cp_snapshot_list; - } else { - header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr); - list = &header->ch_snapshot_list; - } - return list; -} - -static int nilfs_cpfile_set_snapshot(struct inode *cpfile, __u64 cno) -{ - struct buffer_head *header_bh, *curr_bh, *prev_bh, *cp_bh; - struct nilfs_cpfile_header *header; - struct nilfs_checkpoint *cp; - struct nilfs_snapshot_list *list; - __u64 curr, prev; - unsigned long curr_blkoff, prev_blkoff; - void *kaddr; - int ret; - - if (cno == 0) - return -ENOENT; /* checkpoint number 0 is invalid */ - down_write(&NILFS_MDT(cpfile)->mi_sem); - - ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh); - if (ret < 0) - goto out_sem; - kaddr = kmap_atomic(cp_bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr); - if (nilfs_checkpoint_invalid(cp)) { - ret = -ENOENT; - kunmap_atomic(kaddr); - goto out_cp; - } - if (nilfs_checkpoint_snapshot(cp)) { - ret = 0; - kunmap_atomic(kaddr); - goto out_cp; - } - kunmap_atomic(kaddr); - - ret = nilfs_cpfile_get_header_block(cpfile, &header_bh); - if (ret < 0) - goto out_cp; - kaddr = kmap_atomic(header_bh->b_page); - header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr); - list = &header->ch_snapshot_list; - curr_bh = header_bh; - get_bh(curr_bh); - curr = 0; - curr_blkoff = 0; - prev = le64_to_cpu(list->ssl_prev); - while (prev > cno) { - prev_blkoff = nilfs_cpfile_get_blkoff(cpfile, prev); - curr = prev; - if (curr_blkoff != prev_blkoff) { - kunmap_atomic(kaddr); - brelse(curr_bh); - ret = nilfs_cpfile_get_checkpoint_block(cpfile, curr, - 0, &curr_bh); - if (ret < 0) - goto out_header; - kaddr = kmap_atomic(curr_bh->b_page); - } - curr_blkoff = prev_blkoff; - cp = nilfs_cpfile_block_get_checkpoint( - cpfile, curr, curr_bh, kaddr); - list = &cp->cp_snapshot_list; - prev = le64_to_cpu(list->ssl_prev); - } - kunmap_atomic(kaddr); - - if (prev != 0) { - ret = nilfs_cpfile_get_checkpoint_block(cpfile, prev, 0, - &prev_bh); - if (ret < 0) - goto out_curr; - } else { - prev_bh = header_bh; - get_bh(prev_bh); - } - - kaddr = kmap_atomic(curr_bh->b_page); - list = nilfs_cpfile_block_get_snapshot_list( - cpfile, curr, curr_bh, kaddr); - list->ssl_prev = cpu_to_le64(cno); - kunmap_atomic(kaddr); - - kaddr = kmap_atomic(cp_bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr); - cp->cp_snapshot_list.ssl_next = cpu_to_le64(curr); - cp->cp_snapshot_list.ssl_prev = cpu_to_le64(prev); - nilfs_checkpoint_set_snapshot(cp); - kunmap_atomic(kaddr); - - kaddr = kmap_atomic(prev_bh->b_page); - list = nilfs_cpfile_block_get_snapshot_list( - cpfile, prev, prev_bh, kaddr); - list->ssl_next = cpu_to_le64(cno); - kunmap_atomic(kaddr); - - kaddr = kmap_atomic(header_bh->b_page); - header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr); - le64_add_cpu(&header->ch_nsnapshots, 1); - kunmap_atomic(kaddr); - - mark_buffer_dirty(prev_bh); - mark_buffer_dirty(curr_bh); - mark_buffer_dirty(cp_bh); - mark_buffer_dirty(header_bh); - nilfs_mdt_mark_dirty(cpfile); - - brelse(prev_bh); - - out_curr: - brelse(curr_bh); - - out_header: - brelse(header_bh); - - out_cp: - brelse(cp_bh); - - out_sem: - up_write(&NILFS_MDT(cpfile)->mi_sem); - return ret; -} - -static int nilfs_cpfile_clear_snapshot(struct inode *cpfile, __u64 cno) -{ - struct buffer_head *header_bh, *next_bh, *prev_bh, *cp_bh; - struct nilfs_cpfile_header *header; - struct nilfs_checkpoint *cp; - struct nilfs_snapshot_list *list; - __u64 next, prev; - void *kaddr; - int ret; - - if (cno == 0) - return -ENOENT; /* checkpoint number 0 is invalid */ - down_write(&NILFS_MDT(cpfile)->mi_sem); - - ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh); - if (ret < 0) - goto out_sem; - kaddr = kmap_atomic(cp_bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr); - if (nilfs_checkpoint_invalid(cp)) { - ret = -ENOENT; - kunmap_atomic(kaddr); - goto out_cp; - } - if (!nilfs_checkpoint_snapshot(cp)) { - ret = 0; - kunmap_atomic(kaddr); - goto out_cp; - } - - list = &cp->cp_snapshot_list; - next = le64_to_cpu(list->ssl_next); - prev = le64_to_cpu(list->ssl_prev); - kunmap_atomic(kaddr); - - ret = nilfs_cpfile_get_header_block(cpfile, &header_bh); - if (ret < 0) - goto out_cp; - if (next != 0) { - ret = nilfs_cpfile_get_checkpoint_block(cpfile, next, 0, - &next_bh); - if (ret < 0) - goto out_header; - } else { - next_bh = header_bh; - get_bh(next_bh); - } - if (prev != 0) { - ret = nilfs_cpfile_get_checkpoint_block(cpfile, prev, 0, - &prev_bh); - if (ret < 0) - goto out_next; - } else { - prev_bh = header_bh; - get_bh(prev_bh); - } - - kaddr = kmap_atomic(next_bh->b_page); - list = nilfs_cpfile_block_get_snapshot_list( - cpfile, next, next_bh, kaddr); - list->ssl_prev = cpu_to_le64(prev); - kunmap_atomic(kaddr); - - kaddr = kmap_atomic(prev_bh->b_page); - list = nilfs_cpfile_block_get_snapshot_list( - cpfile, prev, prev_bh, kaddr); - list->ssl_next = cpu_to_le64(next); - kunmap_atomic(kaddr); - - kaddr = kmap_atomic(cp_bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr); - cp->cp_snapshot_list.ssl_next = cpu_to_le64(0); - cp->cp_snapshot_list.ssl_prev = cpu_to_le64(0); - nilfs_checkpoint_clear_snapshot(cp); - kunmap_atomic(kaddr); - - kaddr = kmap_atomic(header_bh->b_page); - header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr); - le64_add_cpu(&header->ch_nsnapshots, -1); - kunmap_atomic(kaddr); - - mark_buffer_dirty(next_bh); - mark_buffer_dirty(prev_bh); - mark_buffer_dirty(cp_bh); - mark_buffer_dirty(header_bh); - nilfs_mdt_mark_dirty(cpfile); - - brelse(prev_bh); - - out_next: - brelse(next_bh); - - out_header: - brelse(header_bh); - - out_cp: - brelse(cp_bh); - - out_sem: - up_write(&NILFS_MDT(cpfile)->mi_sem); - return ret; -} - -/** - * nilfs_cpfile_is_snapshot - - * @cpfile: inode of checkpoint file - * @cno: checkpoint number - * - * Description: - * - * Return Value: On success, 1 is returned if the checkpoint specified by - * @cno is a snapshot, or 0 if not. On error, one of the following negative - * error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - * - * %-ENOENT - No such checkpoint. - */ -int nilfs_cpfile_is_snapshot(struct inode *cpfile, __u64 cno) -{ - struct buffer_head *bh; - struct nilfs_checkpoint *cp; - void *kaddr; - int ret; - - /* CP number is invalid if it's zero or larger than the - largest exist one.*/ - if (cno == 0 || cno >= nilfs_mdt_cno(cpfile)) - return -ENOENT; - down_read(&NILFS_MDT(cpfile)->mi_sem); - - ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &bh); - if (ret < 0) - goto out; - kaddr = kmap_atomic(bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr); - if (nilfs_checkpoint_invalid(cp)) - ret = -ENOENT; - else - ret = nilfs_checkpoint_snapshot(cp); - kunmap_atomic(kaddr); - brelse(bh); - - out: - up_read(&NILFS_MDT(cpfile)->mi_sem); - return ret; -} - -/** - * nilfs_cpfile_change_cpmode - change checkpoint mode - * @cpfile: inode of checkpoint file - * @cno: checkpoint number - * @status: mode of checkpoint - * - * Description: nilfs_change_cpmode() changes the mode of the checkpoint - * specified by @cno. The mode @mode is NILFS_CHECKPOINT or NILFS_SNAPSHOT. - * - * Return Value: On success, 0 is returned. On error, one of the following - * negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - * - * %-ENOENT - No such checkpoint. - */ -int nilfs_cpfile_change_cpmode(struct inode *cpfile, __u64 cno, int mode) -{ - int ret; - - switch (mode) { - case NILFS_CHECKPOINT: - if (nilfs_checkpoint_is_mounted(cpfile->i_sb, cno)) - /* - * Current implementation does not have to protect - * plain read-only mounts since they are exclusive - * with a read/write mount and are protected from the - * cleaner. - */ - ret = -EBUSY; - else - ret = nilfs_cpfile_clear_snapshot(cpfile, cno); - return ret; - case NILFS_SNAPSHOT: - return nilfs_cpfile_set_snapshot(cpfile, cno); - default: - return -EINVAL; - } -} - -/** - * nilfs_cpfile_get_stat - get checkpoint statistics - * @cpfile: inode of checkpoint file - * @stat: pointer to a structure of checkpoint statistics - * - * Description: nilfs_cpfile_get_stat() returns information about checkpoints. - * - * Return Value: On success, 0 is returned, and checkpoints information is - * stored in the place pointed by @stat. On error, one of the following - * negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - */ -int nilfs_cpfile_get_stat(struct inode *cpfile, struct nilfs_cpstat *cpstat) -{ - struct buffer_head *bh; - struct nilfs_cpfile_header *header; - void *kaddr; - int ret; - - down_read(&NILFS_MDT(cpfile)->mi_sem); - - ret = nilfs_cpfile_get_header_block(cpfile, &bh); - if (ret < 0) - goto out_sem; - kaddr = kmap_atomic(bh->b_page); - header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr); - cpstat->cs_cno = nilfs_mdt_cno(cpfile); - cpstat->cs_ncps = le64_to_cpu(header->ch_ncheckpoints); - cpstat->cs_nsss = le64_to_cpu(header->ch_nsnapshots); - kunmap_atomic(kaddr); - brelse(bh); - - out_sem: - up_read(&NILFS_MDT(cpfile)->mi_sem); - return ret; -} - -/** - * nilfs_cpfile_read - read or get cpfile inode - * @sb: super block instance - * @cpsize: size of a checkpoint entry - * @raw_inode: on-disk cpfile inode - * @inodep: buffer to store the inode - */ -int nilfs_cpfile_read(struct super_block *sb, size_t cpsize, - struct nilfs_inode *raw_inode, struct inode **inodep) -{ - struct inode *cpfile; - int err; - - cpfile = nilfs_iget_locked(sb, NULL, NILFS_CPFILE_INO); - if (unlikely(!cpfile)) - return -ENOMEM; - if (!(cpfile->i_state & I_NEW)) - goto out; - - err = nilfs_mdt_init(cpfile, NILFS_MDT_GFP, 0); - if (err) - goto failed; - - nilfs_mdt_set_entry_size(cpfile, cpsize, - sizeof(struct nilfs_cpfile_header)); - - err = nilfs_read_inode_common(cpfile, raw_inode); - if (err) - goto failed; - - unlock_new_inode(cpfile); - out: - *inodep = cpfile; - return 0; - failed: - iget_failed(cpfile); - return err; -} diff --git a/ANDROID_3.4.5/fs/nilfs2/cpfile.h b/ANDROID_3.4.5/fs/nilfs2/cpfile.h deleted file mode 100644 index a242b9a3..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/cpfile.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * cpfile.h - NILFS checkpoint file. - * - * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Koji Sato <koji@osrg.net>. - */ - -#ifndef _NILFS_CPFILE_H -#define _NILFS_CPFILE_H - -#include <linux/fs.h> -#include <linux/buffer_head.h> -#include <linux/nilfs2_fs.h> - - -int nilfs_cpfile_get_checkpoint(struct inode *, __u64, int, - struct nilfs_checkpoint **, - struct buffer_head **); -void nilfs_cpfile_put_checkpoint(struct inode *, __u64, struct buffer_head *); -int nilfs_cpfile_delete_checkpoints(struct inode *, __u64, __u64); -int nilfs_cpfile_delete_checkpoint(struct inode *, __u64); -int nilfs_cpfile_change_cpmode(struct inode *, __u64, int); -int nilfs_cpfile_is_snapshot(struct inode *, __u64); -int nilfs_cpfile_get_stat(struct inode *, struct nilfs_cpstat *); -ssize_t nilfs_cpfile_get_cpinfo(struct inode *, __u64 *, int, void *, unsigned, - size_t); - -int nilfs_cpfile_read(struct super_block *sb, size_t cpsize, - struct nilfs_inode *raw_inode, struct inode **inodep); - -#endif /* _NILFS_CPFILE_H */ diff --git a/ANDROID_3.4.5/fs/nilfs2/dat.c b/ANDROID_3.4.5/fs/nilfs2/dat.c deleted file mode 100644 index b5c13f35..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/dat.c +++ /dev/null @@ -1,511 +0,0 @@ -/* - * dat.c - NILFS disk address translation. - * - * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Koji Sato <koji@osrg.net>. - */ - -#include <linux/types.h> -#include <linux/buffer_head.h> -#include <linux/string.h> -#include <linux/errno.h> -#include "nilfs.h" -#include "mdt.h" -#include "alloc.h" -#include "dat.h" - - -#define NILFS_CNO_MIN ((__u64)1) -#define NILFS_CNO_MAX (~(__u64)0) - -struct nilfs_dat_info { - struct nilfs_mdt_info mi; - struct nilfs_palloc_cache palloc_cache; - struct nilfs_shadow_map shadow; -}; - -static inline struct nilfs_dat_info *NILFS_DAT_I(struct inode *dat) -{ - return (struct nilfs_dat_info *)NILFS_MDT(dat); -} - -static int nilfs_dat_prepare_entry(struct inode *dat, - struct nilfs_palloc_req *req, int create) -{ - return nilfs_palloc_get_entry_block(dat, req->pr_entry_nr, - create, &req->pr_entry_bh); -} - -static void nilfs_dat_commit_entry(struct inode *dat, - struct nilfs_palloc_req *req) -{ - mark_buffer_dirty(req->pr_entry_bh); - nilfs_mdt_mark_dirty(dat); - brelse(req->pr_entry_bh); -} - -static void nilfs_dat_abort_entry(struct inode *dat, - struct nilfs_palloc_req *req) -{ - brelse(req->pr_entry_bh); -} - -int nilfs_dat_prepare_alloc(struct inode *dat, struct nilfs_palloc_req *req) -{ - int ret; - - ret = nilfs_palloc_prepare_alloc_entry(dat, req); - if (ret < 0) - return ret; - - ret = nilfs_dat_prepare_entry(dat, req, 1); - if (ret < 0) - nilfs_palloc_abort_alloc_entry(dat, req); - - return ret; -} - -void nilfs_dat_commit_alloc(struct inode *dat, struct nilfs_palloc_req *req) -{ - struct nilfs_dat_entry *entry; - void *kaddr; - - kaddr = kmap_atomic(req->pr_entry_bh->b_page); - entry = nilfs_palloc_block_get_entry(dat, req->pr_entry_nr, - req->pr_entry_bh, kaddr); - entry->de_start = cpu_to_le64(NILFS_CNO_MIN); - entry->de_end = cpu_to_le64(NILFS_CNO_MAX); - entry->de_blocknr = cpu_to_le64(0); - kunmap_atomic(kaddr); - - nilfs_palloc_commit_alloc_entry(dat, req); - nilfs_dat_commit_entry(dat, req); -} - -void nilfs_dat_abort_alloc(struct inode *dat, struct nilfs_palloc_req *req) -{ - nilfs_dat_abort_entry(dat, req); - nilfs_palloc_abort_alloc_entry(dat, req); -} - -static void nilfs_dat_commit_free(struct inode *dat, - struct nilfs_palloc_req *req) -{ - struct nilfs_dat_entry *entry; - void *kaddr; - - kaddr = kmap_atomic(req->pr_entry_bh->b_page); - entry = nilfs_palloc_block_get_entry(dat, req->pr_entry_nr, - req->pr_entry_bh, kaddr); - entry->de_start = cpu_to_le64(NILFS_CNO_MIN); - entry->de_end = cpu_to_le64(NILFS_CNO_MIN); - entry->de_blocknr = cpu_to_le64(0); - kunmap_atomic(kaddr); - - nilfs_dat_commit_entry(dat, req); - nilfs_palloc_commit_free_entry(dat, req); -} - -int nilfs_dat_prepare_start(struct inode *dat, struct nilfs_palloc_req *req) -{ - int ret; - - ret = nilfs_dat_prepare_entry(dat, req, 0); - WARN_ON(ret == -ENOENT); - return ret; -} - -void nilfs_dat_commit_start(struct inode *dat, struct nilfs_palloc_req *req, - sector_t blocknr) -{ - struct nilfs_dat_entry *entry; - void *kaddr; - - kaddr = kmap_atomic(req->pr_entry_bh->b_page); - entry = nilfs_palloc_block_get_entry(dat, req->pr_entry_nr, - req->pr_entry_bh, kaddr); - entry->de_start = cpu_to_le64(nilfs_mdt_cno(dat)); - entry->de_blocknr = cpu_to_le64(blocknr); - kunmap_atomic(kaddr); - - nilfs_dat_commit_entry(dat, req); -} - -int nilfs_dat_prepare_end(struct inode *dat, struct nilfs_palloc_req *req) -{ - struct nilfs_dat_entry *entry; - __u64 start; - sector_t blocknr; - void *kaddr; - int ret; - - ret = nilfs_dat_prepare_entry(dat, req, 0); - if (ret < 0) { - WARN_ON(ret == -ENOENT); - return ret; - } - - kaddr = kmap_atomic(req->pr_entry_bh->b_page); - entry = nilfs_palloc_block_get_entry(dat, req->pr_entry_nr, - req->pr_entry_bh, kaddr); - start = le64_to_cpu(entry->de_start); - blocknr = le64_to_cpu(entry->de_blocknr); - kunmap_atomic(kaddr); - - if (blocknr == 0) { - ret = nilfs_palloc_prepare_free_entry(dat, req); - if (ret < 0) { - nilfs_dat_abort_entry(dat, req); - return ret; - } - } - - return 0; -} - -void nilfs_dat_commit_end(struct inode *dat, struct nilfs_palloc_req *req, - int dead) -{ - struct nilfs_dat_entry *entry; - __u64 start, end; - sector_t blocknr; - void *kaddr; - - kaddr = kmap_atomic(req->pr_entry_bh->b_page); - entry = nilfs_palloc_block_get_entry(dat, req->pr_entry_nr, - req->pr_entry_bh, kaddr); - end = start = le64_to_cpu(entry->de_start); - if (!dead) { - end = nilfs_mdt_cno(dat); - WARN_ON(start > end); - } - entry->de_end = cpu_to_le64(end); - blocknr = le64_to_cpu(entry->de_blocknr); - kunmap_atomic(kaddr); - - if (blocknr == 0) - nilfs_dat_commit_free(dat, req); - else - nilfs_dat_commit_entry(dat, req); -} - -void nilfs_dat_abort_end(struct inode *dat, struct nilfs_palloc_req *req) -{ - struct nilfs_dat_entry *entry; - __u64 start; - sector_t blocknr; - void *kaddr; - - kaddr = kmap_atomic(req->pr_entry_bh->b_page); - entry = nilfs_palloc_block_get_entry(dat, req->pr_entry_nr, - req->pr_entry_bh, kaddr); - start = le64_to_cpu(entry->de_start); - blocknr = le64_to_cpu(entry->de_blocknr); - kunmap_atomic(kaddr); - - if (start == nilfs_mdt_cno(dat) && blocknr == 0) - nilfs_palloc_abort_free_entry(dat, req); - nilfs_dat_abort_entry(dat, req); -} - -int nilfs_dat_prepare_update(struct inode *dat, - struct nilfs_palloc_req *oldreq, - struct nilfs_palloc_req *newreq) -{ - int ret; - - ret = nilfs_dat_prepare_end(dat, oldreq); - if (!ret) { - ret = nilfs_dat_prepare_alloc(dat, newreq); - if (ret < 0) - nilfs_dat_abort_end(dat, oldreq); - } - return ret; -} - -void nilfs_dat_commit_update(struct inode *dat, - struct nilfs_palloc_req *oldreq, - struct nilfs_palloc_req *newreq, int dead) -{ - nilfs_dat_commit_end(dat, oldreq, dead); - nilfs_dat_commit_alloc(dat, newreq); -} - -void nilfs_dat_abort_update(struct inode *dat, - struct nilfs_palloc_req *oldreq, - struct nilfs_palloc_req *newreq) -{ - nilfs_dat_abort_end(dat, oldreq); - nilfs_dat_abort_alloc(dat, newreq); -} - -/** - * nilfs_dat_mark_dirty - - * @dat: DAT file inode - * @vblocknr: virtual block number - * - * Description: - * - * Return Value: On success, 0 is returned. On error, one of the following - * negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - */ -int nilfs_dat_mark_dirty(struct inode *dat, __u64 vblocknr) -{ - struct nilfs_palloc_req req; - int ret; - - req.pr_entry_nr = vblocknr; - ret = nilfs_dat_prepare_entry(dat, &req, 0); - if (ret == 0) - nilfs_dat_commit_entry(dat, &req); - return ret; -} - -/** - * nilfs_dat_freev - free virtual block numbers - * @dat: DAT file inode - * @vblocknrs: array of virtual block numbers - * @nitems: number of virtual block numbers - * - * Description: nilfs_dat_freev() frees the virtual block numbers specified by - * @vblocknrs and @nitems. - * - * Return Value: On success, 0 is returned. On error, one of the following - * negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - * - * %-ENOENT - The virtual block number have not been allocated. - */ -int nilfs_dat_freev(struct inode *dat, __u64 *vblocknrs, size_t nitems) -{ - return nilfs_palloc_freev(dat, vblocknrs, nitems); -} - -/** - * nilfs_dat_move - change a block number - * @dat: DAT file inode - * @vblocknr: virtual block number - * @blocknr: block number - * - * Description: nilfs_dat_move() changes the block number associated with - * @vblocknr to @blocknr. - * - * Return Value: On success, 0 is returned. On error, one of the following - * negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - */ -int nilfs_dat_move(struct inode *dat, __u64 vblocknr, sector_t blocknr) -{ - struct buffer_head *entry_bh; - struct nilfs_dat_entry *entry; - void *kaddr; - int ret; - - ret = nilfs_palloc_get_entry_block(dat, vblocknr, 0, &entry_bh); - if (ret < 0) - return ret; - - /* - * The given disk block number (blocknr) is not yet written to - * the device at this point. - * - * To prevent nilfs_dat_translate() from returning the - * uncommitted block number, this makes a copy of the entry - * buffer and redirects nilfs_dat_translate() to the copy. - */ - if (!buffer_nilfs_redirected(entry_bh)) { - ret = nilfs_mdt_freeze_buffer(dat, entry_bh); - if (ret) { - brelse(entry_bh); - return ret; - } - } - - kaddr = kmap_atomic(entry_bh->b_page); - entry = nilfs_palloc_block_get_entry(dat, vblocknr, entry_bh, kaddr); - if (unlikely(entry->de_blocknr == cpu_to_le64(0))) { - printk(KERN_CRIT "%s: vbn = %llu, [%llu, %llu)\n", __func__, - (unsigned long long)vblocknr, - (unsigned long long)le64_to_cpu(entry->de_start), - (unsigned long long)le64_to_cpu(entry->de_end)); - kunmap_atomic(kaddr); - brelse(entry_bh); - return -EINVAL; - } - WARN_ON(blocknr == 0); - entry->de_blocknr = cpu_to_le64(blocknr); - kunmap_atomic(kaddr); - - mark_buffer_dirty(entry_bh); - nilfs_mdt_mark_dirty(dat); - - brelse(entry_bh); - - return 0; -} - -/** - * nilfs_dat_translate - translate a virtual block number to a block number - * @dat: DAT file inode - * @vblocknr: virtual block number - * @blocknrp: pointer to a block number - * - * Description: nilfs_dat_translate() maps the virtual block number @vblocknr - * to the corresponding block number. - * - * Return Value: On success, 0 is returned and the block number associated - * with @vblocknr is stored in the place pointed by @blocknrp. On error, one - * of the following negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - * - * %-ENOENT - A block number associated with @vblocknr does not exist. - */ -int nilfs_dat_translate(struct inode *dat, __u64 vblocknr, sector_t *blocknrp) -{ - struct buffer_head *entry_bh, *bh; - struct nilfs_dat_entry *entry; - sector_t blocknr; - void *kaddr; - int ret; - - ret = nilfs_palloc_get_entry_block(dat, vblocknr, 0, &entry_bh); - if (ret < 0) - return ret; - - if (!nilfs_doing_gc() && buffer_nilfs_redirected(entry_bh)) { - bh = nilfs_mdt_get_frozen_buffer(dat, entry_bh); - if (bh) { - WARN_ON(!buffer_uptodate(bh)); - brelse(entry_bh); - entry_bh = bh; - } - } - - kaddr = kmap_atomic(entry_bh->b_page); - entry = nilfs_palloc_block_get_entry(dat, vblocknr, entry_bh, kaddr); - blocknr = le64_to_cpu(entry->de_blocknr); - if (blocknr == 0) { - ret = -ENOENT; - goto out; - } - *blocknrp = blocknr; - - out: - kunmap_atomic(kaddr); - brelse(entry_bh); - return ret; -} - -ssize_t nilfs_dat_get_vinfo(struct inode *dat, void *buf, unsigned visz, - size_t nvi) -{ - struct buffer_head *entry_bh; - struct nilfs_dat_entry *entry; - struct nilfs_vinfo *vinfo = buf; - __u64 first, last; - void *kaddr; - unsigned long entries_per_block = NILFS_MDT(dat)->mi_entries_per_block; - int i, j, n, ret; - - for (i = 0; i < nvi; i += n) { - ret = nilfs_palloc_get_entry_block(dat, vinfo->vi_vblocknr, - 0, &entry_bh); - if (ret < 0) - return ret; - kaddr = kmap_atomic(entry_bh->b_page); - /* last virtual block number in this block */ - first = vinfo->vi_vblocknr; - do_div(first, entries_per_block); - first *= entries_per_block; - last = first + entries_per_block - 1; - for (j = i, n = 0; - j < nvi && vinfo->vi_vblocknr >= first && - vinfo->vi_vblocknr <= last; - j++, n++, vinfo = (void *)vinfo + visz) { - entry = nilfs_palloc_block_get_entry( - dat, vinfo->vi_vblocknr, entry_bh, kaddr); - vinfo->vi_start = le64_to_cpu(entry->de_start); - vinfo->vi_end = le64_to_cpu(entry->de_end); - vinfo->vi_blocknr = le64_to_cpu(entry->de_blocknr); - } - kunmap_atomic(kaddr); - brelse(entry_bh); - } - - return nvi; -} - -/** - * nilfs_dat_read - read or get dat inode - * @sb: super block instance - * @entry_size: size of a dat entry - * @raw_inode: on-disk dat inode - * @inodep: buffer to store the inode - */ -int nilfs_dat_read(struct super_block *sb, size_t entry_size, - struct nilfs_inode *raw_inode, struct inode **inodep) -{ - static struct lock_class_key dat_lock_key; - struct inode *dat; - struct nilfs_dat_info *di; - int err; - - dat = nilfs_iget_locked(sb, NULL, NILFS_DAT_INO); - if (unlikely(!dat)) - return -ENOMEM; - if (!(dat->i_state & I_NEW)) - goto out; - - err = nilfs_mdt_init(dat, NILFS_MDT_GFP, sizeof(*di)); - if (err) - goto failed; - - err = nilfs_palloc_init_blockgroup(dat, entry_size); - if (err) - goto failed; - - di = NILFS_DAT_I(dat); - lockdep_set_class(&di->mi.mi_sem, &dat_lock_key); - nilfs_palloc_setup_cache(dat, &di->palloc_cache); - nilfs_mdt_setup_shadow_map(dat, &di->shadow); - - err = nilfs_read_inode_common(dat, raw_inode); - if (err) - goto failed; - - unlock_new_inode(dat); - out: - *inodep = dat; - return 0; - failed: - iget_failed(dat); - return err; -} diff --git a/ANDROID_3.4.5/fs/nilfs2/dat.h b/ANDROID_3.4.5/fs/nilfs2/dat.h deleted file mode 100644 index cbd8e973..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/dat.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * dat.h - NILFS disk address translation. - * - * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Koji Sato <koji@osrg.net>. - */ - -#ifndef _NILFS_DAT_H -#define _NILFS_DAT_H - -#include <linux/types.h> -#include <linux/buffer_head.h> -#include <linux/fs.h> - - -struct nilfs_palloc_req; - -int nilfs_dat_translate(struct inode *, __u64, sector_t *); - -int nilfs_dat_prepare_alloc(struct inode *, struct nilfs_palloc_req *); -void nilfs_dat_commit_alloc(struct inode *, struct nilfs_palloc_req *); -void nilfs_dat_abort_alloc(struct inode *, struct nilfs_palloc_req *); -int nilfs_dat_prepare_start(struct inode *, struct nilfs_palloc_req *); -void nilfs_dat_commit_start(struct inode *, struct nilfs_palloc_req *, - sector_t); -int nilfs_dat_prepare_end(struct inode *, struct nilfs_palloc_req *); -void nilfs_dat_commit_end(struct inode *, struct nilfs_palloc_req *, int); -void nilfs_dat_abort_end(struct inode *, struct nilfs_palloc_req *); -int nilfs_dat_prepare_update(struct inode *, struct nilfs_palloc_req *, - struct nilfs_palloc_req *); -void nilfs_dat_commit_update(struct inode *, struct nilfs_palloc_req *, - struct nilfs_palloc_req *, int); -void nilfs_dat_abort_update(struct inode *, struct nilfs_palloc_req *, - struct nilfs_palloc_req *); - -int nilfs_dat_mark_dirty(struct inode *, __u64); -int nilfs_dat_freev(struct inode *, __u64 *, size_t); -int nilfs_dat_move(struct inode *, __u64, sector_t); -ssize_t nilfs_dat_get_vinfo(struct inode *, void *, unsigned, size_t); - -int nilfs_dat_read(struct super_block *sb, size_t entry_size, - struct nilfs_inode *raw_inode, struct inode **inodep); - -#endif /* _NILFS_DAT_H */ diff --git a/ANDROID_3.4.5/fs/nilfs2/dir.c b/ANDROID_3.4.5/fs/nilfs2/dir.c deleted file mode 100644 index df1a7fb2..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/dir.c +++ /dev/null @@ -1,688 +0,0 @@ -/* - * dir.c - NILFS directory entry operations - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Modified for NILFS by Amagai Yoshiji <amagai@osrg.net> - */ -/* - * linux/fs/ext2/dir.c - * - * Copyright (C) 1992, 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) - * - * from - * - * linux/fs/minix/dir.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * - * ext2 directory handling functions - * - * Big-endian to little-endian byte-swapping/bitmaps by - * David S. Miller (davem@caip.rutgers.edu), 1995 - * - * All code that works with directory layout had been switched to pagecache - * and moved here. AV - */ - -#include <linux/pagemap.h> -#include "nilfs.h" -#include "page.h" - -/* - * nilfs uses block-sized chunks. Arguably, sector-sized ones would be - * more robust, but we have what we have - */ -static inline unsigned nilfs_chunk_size(struct inode *inode) -{ - return inode->i_sb->s_blocksize; -} - -static inline void nilfs_put_page(struct page *page) -{ - kunmap(page); - page_cache_release(page); -} - -static inline unsigned long dir_pages(struct inode *inode) -{ - return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT; -} - -/* - * Return the offset into page `page_nr' of the last valid - * byte in that page, plus one. - */ -static unsigned nilfs_last_byte(struct inode *inode, unsigned long page_nr) -{ - unsigned last_byte = inode->i_size; - - last_byte -= page_nr << PAGE_CACHE_SHIFT; - if (last_byte > PAGE_CACHE_SIZE) - last_byte = PAGE_CACHE_SIZE; - return last_byte; -} - -static int nilfs_prepare_chunk(struct page *page, unsigned from, unsigned to) -{ - loff_t pos = page_offset(page) + from; - return __block_write_begin(page, pos, to - from, nilfs_get_block); -} - -static void nilfs_commit_chunk(struct page *page, - struct address_space *mapping, - unsigned from, unsigned to) -{ - struct inode *dir = mapping->host; - loff_t pos = page_offset(page) + from; - unsigned len = to - from; - unsigned nr_dirty, copied; - int err; - - nr_dirty = nilfs_page_count_clean_buffers(page, from, to); - copied = block_write_end(NULL, mapping, pos, len, len, page, NULL); - if (pos + copied > dir->i_size) - i_size_write(dir, pos + copied); - if (IS_DIRSYNC(dir)) - nilfs_set_transaction_flag(NILFS_TI_SYNC); - err = nilfs_set_file_dirty(dir, nr_dirty); - WARN_ON(err); /* do not happen */ - unlock_page(page); -} - -static void nilfs_check_page(struct page *page) -{ - struct inode *dir = page->mapping->host; - struct super_block *sb = dir->i_sb; - unsigned chunk_size = nilfs_chunk_size(dir); - char *kaddr = page_address(page); - unsigned offs, rec_len; - unsigned limit = PAGE_CACHE_SIZE; - struct nilfs_dir_entry *p; - char *error; - - if ((dir->i_size >> PAGE_CACHE_SHIFT) == page->index) { - limit = dir->i_size & ~PAGE_CACHE_MASK; - if (limit & (chunk_size - 1)) - goto Ebadsize; - if (!limit) - goto out; - } - for (offs = 0; offs <= limit - NILFS_DIR_REC_LEN(1); offs += rec_len) { - p = (struct nilfs_dir_entry *)(kaddr + offs); - rec_len = nilfs_rec_len_from_disk(p->rec_len); - - if (rec_len < NILFS_DIR_REC_LEN(1)) - goto Eshort; - if (rec_len & 3) - goto Ealign; - if (rec_len < NILFS_DIR_REC_LEN(p->name_len)) - goto Enamelen; - if (((offs + rec_len - 1) ^ offs) & ~(chunk_size-1)) - goto Espan; - } - if (offs != limit) - goto Eend; -out: - SetPageChecked(page); - return; - - /* Too bad, we had an error */ - -Ebadsize: - nilfs_error(sb, "nilfs_check_page", - "size of directory #%lu is not a multiple of chunk size", - dir->i_ino - ); - goto fail; -Eshort: - error = "rec_len is smaller than minimal"; - goto bad_entry; -Ealign: - error = "unaligned directory entry"; - goto bad_entry; -Enamelen: - error = "rec_len is too small for name_len"; - goto bad_entry; -Espan: - error = "directory entry across blocks"; -bad_entry: - nilfs_error(sb, "nilfs_check_page", "bad entry in directory #%lu: %s - " - "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", - dir->i_ino, error, (page->index<<PAGE_CACHE_SHIFT)+offs, - (unsigned long) le64_to_cpu(p->inode), - rec_len, p->name_len); - goto fail; -Eend: - p = (struct nilfs_dir_entry *)(kaddr + offs); - nilfs_error(sb, "nilfs_check_page", - "entry in directory #%lu spans the page boundary" - "offset=%lu, inode=%lu", - dir->i_ino, (page->index<<PAGE_CACHE_SHIFT)+offs, - (unsigned long) le64_to_cpu(p->inode)); -fail: - SetPageChecked(page); - SetPageError(page); -} - -static struct page *nilfs_get_page(struct inode *dir, unsigned long n) -{ - struct address_space *mapping = dir->i_mapping; - struct page *page = read_mapping_page(mapping, n, NULL); - - if (!IS_ERR(page)) { - kmap(page); - if (!PageChecked(page)) - nilfs_check_page(page); - if (PageError(page)) - goto fail; - } - return page; - -fail: - nilfs_put_page(page); - return ERR_PTR(-EIO); -} - -/* - * NOTE! unlike strncmp, nilfs_match returns 1 for success, 0 for failure. - * - * len <= NILFS_NAME_LEN and de != NULL are guaranteed by caller. - */ -static int -nilfs_match(int len, const unsigned char *name, struct nilfs_dir_entry *de) -{ - if (len != de->name_len) - return 0; - if (!de->inode) - return 0; - return !memcmp(name, de->name, len); -} - -/* - * p is at least 6 bytes before the end of page - */ -static struct nilfs_dir_entry *nilfs_next_entry(struct nilfs_dir_entry *p) -{ - return (struct nilfs_dir_entry *)((char *)p + - nilfs_rec_len_from_disk(p->rec_len)); -} - -static unsigned char -nilfs_filetype_table[NILFS_FT_MAX] = { - [NILFS_FT_UNKNOWN] = DT_UNKNOWN, - [NILFS_FT_REG_FILE] = DT_REG, - [NILFS_FT_DIR] = DT_DIR, - [NILFS_FT_CHRDEV] = DT_CHR, - [NILFS_FT_BLKDEV] = DT_BLK, - [NILFS_FT_FIFO] = DT_FIFO, - [NILFS_FT_SOCK] = DT_SOCK, - [NILFS_FT_SYMLINK] = DT_LNK, -}; - -#define S_SHIFT 12 -static unsigned char -nilfs_type_by_mode[S_IFMT >> S_SHIFT] = { - [S_IFREG >> S_SHIFT] = NILFS_FT_REG_FILE, - [S_IFDIR >> S_SHIFT] = NILFS_FT_DIR, - [S_IFCHR >> S_SHIFT] = NILFS_FT_CHRDEV, - [S_IFBLK >> S_SHIFT] = NILFS_FT_BLKDEV, - [S_IFIFO >> S_SHIFT] = NILFS_FT_FIFO, - [S_IFSOCK >> S_SHIFT] = NILFS_FT_SOCK, - [S_IFLNK >> S_SHIFT] = NILFS_FT_SYMLINK, -}; - -static void nilfs_set_de_type(struct nilfs_dir_entry *de, struct inode *inode) -{ - umode_t mode = inode->i_mode; - - de->file_type = nilfs_type_by_mode[(mode & S_IFMT)>>S_SHIFT]; -} - -static int nilfs_readdir(struct file *filp, void *dirent, filldir_t filldir) -{ - loff_t pos = filp->f_pos; - struct inode *inode = filp->f_dentry->d_inode; - struct super_block *sb = inode->i_sb; - unsigned int offset = pos & ~PAGE_CACHE_MASK; - unsigned long n = pos >> PAGE_CACHE_SHIFT; - unsigned long npages = dir_pages(inode); -/* unsigned chunk_mask = ~(nilfs_chunk_size(inode)-1); */ - unsigned char *types = NULL; - int ret; - - if (pos > inode->i_size - NILFS_DIR_REC_LEN(1)) - goto success; - - types = nilfs_filetype_table; - - for ( ; n < npages; n++, offset = 0) { - char *kaddr, *limit; - struct nilfs_dir_entry *de; - struct page *page = nilfs_get_page(inode, n); - - if (IS_ERR(page)) { - nilfs_error(sb, __func__, "bad page in #%lu", - inode->i_ino); - filp->f_pos += PAGE_CACHE_SIZE - offset; - ret = -EIO; - goto done; - } - kaddr = page_address(page); - de = (struct nilfs_dir_entry *)(kaddr + offset); - limit = kaddr + nilfs_last_byte(inode, n) - - NILFS_DIR_REC_LEN(1); - for ( ; (char *)de <= limit; de = nilfs_next_entry(de)) { - if (de->rec_len == 0) { - nilfs_error(sb, __func__, - "zero-length directory entry"); - ret = -EIO; - nilfs_put_page(page); - goto done; - } - if (de->inode) { - int over; - unsigned char d_type = DT_UNKNOWN; - - if (types && de->file_type < NILFS_FT_MAX) - d_type = types[de->file_type]; - - offset = (char *)de - kaddr; - over = filldir(dirent, de->name, de->name_len, - (n<<PAGE_CACHE_SHIFT) | offset, - le64_to_cpu(de->inode), d_type); - if (over) { - nilfs_put_page(page); - goto success; - } - } - filp->f_pos += nilfs_rec_len_from_disk(de->rec_len); - } - nilfs_put_page(page); - } - -success: - ret = 0; -done: - return ret; -} - -/* - * nilfs_find_entry() - * - * finds an entry in the specified directory with the wanted name. It - * returns the page in which the entry was found, and the entry itself - * (as a parameter - res_dir). Page is returned mapped and unlocked. - * Entry is guaranteed to be valid. - */ -struct nilfs_dir_entry * -nilfs_find_entry(struct inode *dir, const struct qstr *qstr, - struct page **res_page) -{ - const unsigned char *name = qstr->name; - int namelen = qstr->len; - unsigned reclen = NILFS_DIR_REC_LEN(namelen); - unsigned long start, n; - unsigned long npages = dir_pages(dir); - struct page *page = NULL; - struct nilfs_inode_info *ei = NILFS_I(dir); - struct nilfs_dir_entry *de; - - if (npages == 0) - goto out; - - /* OFFSET_CACHE */ - *res_page = NULL; - - start = ei->i_dir_start_lookup; - if (start >= npages) - start = 0; - n = start; - do { - char *kaddr; - page = nilfs_get_page(dir, n); - if (!IS_ERR(page)) { - kaddr = page_address(page); - de = (struct nilfs_dir_entry *)kaddr; - kaddr += nilfs_last_byte(dir, n) - reclen; - while ((char *) de <= kaddr) { - if (de->rec_len == 0) { - nilfs_error(dir->i_sb, __func__, - "zero-length directory entry"); - nilfs_put_page(page); - goto out; - } - if (nilfs_match(namelen, name, de)) - goto found; - de = nilfs_next_entry(de); - } - nilfs_put_page(page); - } - if (++n >= npages) - n = 0; - /* next page is past the blocks we've got */ - if (unlikely(n > (dir->i_blocks >> (PAGE_CACHE_SHIFT - 9)))) { - nilfs_error(dir->i_sb, __func__, - "dir %lu size %lld exceeds block count %llu", - dir->i_ino, dir->i_size, - (unsigned long long)dir->i_blocks); - goto out; - } - } while (n != start); -out: - return NULL; - -found: - *res_page = page; - ei->i_dir_start_lookup = n; - return de; -} - -struct nilfs_dir_entry *nilfs_dotdot(struct inode *dir, struct page **p) -{ - struct page *page = nilfs_get_page(dir, 0); - struct nilfs_dir_entry *de = NULL; - - if (!IS_ERR(page)) { - de = nilfs_next_entry( - (struct nilfs_dir_entry *)page_address(page)); - *p = page; - } - return de; -} - -ino_t nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr) -{ - ino_t res = 0; - struct nilfs_dir_entry *de; - struct page *page; - - de = nilfs_find_entry(dir, qstr, &page); - if (de) { - res = le64_to_cpu(de->inode); - kunmap(page); - page_cache_release(page); - } - return res; -} - -/* Releases the page */ -void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de, - struct page *page, struct inode *inode) -{ - unsigned from = (char *) de - (char *) page_address(page); - unsigned to = from + nilfs_rec_len_from_disk(de->rec_len); - struct address_space *mapping = page->mapping; - int err; - - lock_page(page); - err = nilfs_prepare_chunk(page, from, to); - BUG_ON(err); - de->inode = cpu_to_le64(inode->i_ino); - nilfs_set_de_type(de, inode); - nilfs_commit_chunk(page, mapping, from, to); - nilfs_put_page(page); - dir->i_mtime = dir->i_ctime = CURRENT_TIME; -} - -/* - * Parent is locked. - */ -int nilfs_add_link(struct dentry *dentry, struct inode *inode) -{ - struct inode *dir = dentry->d_parent->d_inode; - const unsigned char *name = dentry->d_name.name; - int namelen = dentry->d_name.len; - unsigned chunk_size = nilfs_chunk_size(dir); - unsigned reclen = NILFS_DIR_REC_LEN(namelen); - unsigned short rec_len, name_len; - struct page *page = NULL; - struct nilfs_dir_entry *de; - unsigned long npages = dir_pages(dir); - unsigned long n; - char *kaddr; - unsigned from, to; - int err; - - /* - * We take care of directory expansion in the same loop. - * This code plays outside i_size, so it locks the page - * to protect that region. - */ - for (n = 0; n <= npages; n++) { - char *dir_end; - - page = nilfs_get_page(dir, n); - err = PTR_ERR(page); - if (IS_ERR(page)) - goto out; - lock_page(page); - kaddr = page_address(page); - dir_end = kaddr + nilfs_last_byte(dir, n); - de = (struct nilfs_dir_entry *)kaddr; - kaddr += PAGE_CACHE_SIZE - reclen; - while ((char *)de <= kaddr) { - if ((char *)de == dir_end) { - /* We hit i_size */ - name_len = 0; - rec_len = chunk_size; - de->rec_len = nilfs_rec_len_to_disk(chunk_size); - de->inode = 0; - goto got_it; - } - if (de->rec_len == 0) { - nilfs_error(dir->i_sb, __func__, - "zero-length directory entry"); - err = -EIO; - goto out_unlock; - } - err = -EEXIST; - if (nilfs_match(namelen, name, de)) - goto out_unlock; - name_len = NILFS_DIR_REC_LEN(de->name_len); - rec_len = nilfs_rec_len_from_disk(de->rec_len); - if (!de->inode && rec_len >= reclen) - goto got_it; - if (rec_len >= name_len + reclen) - goto got_it; - de = (struct nilfs_dir_entry *)((char *)de + rec_len); - } - unlock_page(page); - nilfs_put_page(page); - } - BUG(); - return -EINVAL; - -got_it: - from = (char *)de - (char *)page_address(page); - to = from + rec_len; - err = nilfs_prepare_chunk(page, from, to); - if (err) - goto out_unlock; - if (de->inode) { - struct nilfs_dir_entry *de1; - - de1 = (struct nilfs_dir_entry *)((char *)de + name_len); - de1->rec_len = nilfs_rec_len_to_disk(rec_len - name_len); - de->rec_len = nilfs_rec_len_to_disk(name_len); - de = de1; - } - de->name_len = namelen; - memcpy(de->name, name, namelen); - de->inode = cpu_to_le64(inode->i_ino); - nilfs_set_de_type(de, inode); - nilfs_commit_chunk(page, page->mapping, from, to); - dir->i_mtime = dir->i_ctime = CURRENT_TIME; - nilfs_mark_inode_dirty(dir); - /* OFFSET_CACHE */ -out_put: - nilfs_put_page(page); -out: - return err; -out_unlock: - unlock_page(page); - goto out_put; -} - -/* - * nilfs_delete_entry deletes a directory entry by merging it with the - * previous entry. Page is up-to-date. Releases the page. - */ -int nilfs_delete_entry(struct nilfs_dir_entry *dir, struct page *page) -{ - struct address_space *mapping = page->mapping; - struct inode *inode = mapping->host; - char *kaddr = page_address(page); - unsigned from = ((char *)dir - kaddr) & ~(nilfs_chunk_size(inode) - 1); - unsigned to = ((char *)dir - kaddr) + - nilfs_rec_len_from_disk(dir->rec_len); - struct nilfs_dir_entry *pde = NULL; - struct nilfs_dir_entry *de = (struct nilfs_dir_entry *)(kaddr + from); - int err; - - while ((char *)de < (char *)dir) { - if (de->rec_len == 0) { - nilfs_error(inode->i_sb, __func__, - "zero-length directory entry"); - err = -EIO; - goto out; - } - pde = de; - de = nilfs_next_entry(de); - } - if (pde) - from = (char *)pde - (char *)page_address(page); - lock_page(page); - err = nilfs_prepare_chunk(page, from, to); - BUG_ON(err); - if (pde) - pde->rec_len = nilfs_rec_len_to_disk(to - from); - dir->inode = 0; - nilfs_commit_chunk(page, mapping, from, to); - inode->i_ctime = inode->i_mtime = CURRENT_TIME; -out: - nilfs_put_page(page); - return err; -} - -/* - * Set the first fragment of directory. - */ -int nilfs_make_empty(struct inode *inode, struct inode *parent) -{ - struct address_space *mapping = inode->i_mapping; - struct page *page = grab_cache_page(mapping, 0); - unsigned chunk_size = nilfs_chunk_size(inode); - struct nilfs_dir_entry *de; - int err; - void *kaddr; - - if (!page) - return -ENOMEM; - - err = nilfs_prepare_chunk(page, 0, chunk_size); - if (unlikely(err)) { - unlock_page(page); - goto fail; - } - kaddr = kmap_atomic(page); - memset(kaddr, 0, chunk_size); - de = (struct nilfs_dir_entry *)kaddr; - de->name_len = 1; - de->rec_len = nilfs_rec_len_to_disk(NILFS_DIR_REC_LEN(1)); - memcpy(de->name, ".\0\0", 4); - de->inode = cpu_to_le64(inode->i_ino); - nilfs_set_de_type(de, inode); - - de = (struct nilfs_dir_entry *)(kaddr + NILFS_DIR_REC_LEN(1)); - de->name_len = 2; - de->rec_len = nilfs_rec_len_to_disk(chunk_size - NILFS_DIR_REC_LEN(1)); - de->inode = cpu_to_le64(parent->i_ino); - memcpy(de->name, "..\0", 4); - nilfs_set_de_type(de, inode); - kunmap_atomic(kaddr); - nilfs_commit_chunk(page, mapping, 0, chunk_size); -fail: - page_cache_release(page); - return err; -} - -/* - * routine to check that the specified directory is empty (for rmdir) - */ -int nilfs_empty_dir(struct inode *inode) -{ - struct page *page = NULL; - unsigned long i, npages = dir_pages(inode); - - for (i = 0; i < npages; i++) { - char *kaddr; - struct nilfs_dir_entry *de; - - page = nilfs_get_page(inode, i); - if (IS_ERR(page)) - continue; - - kaddr = page_address(page); - de = (struct nilfs_dir_entry *)kaddr; - kaddr += nilfs_last_byte(inode, i) - NILFS_DIR_REC_LEN(1); - - while ((char *)de <= kaddr) { - if (de->rec_len == 0) { - nilfs_error(inode->i_sb, __func__, - "zero-length directory entry " - "(kaddr=%p, de=%p)\n", kaddr, de); - goto not_empty; - } - if (de->inode != 0) { - /* check for . and .. */ - if (de->name[0] != '.') - goto not_empty; - if (de->name_len > 2) - goto not_empty; - if (de->name_len < 2) { - if (de->inode != - cpu_to_le64(inode->i_ino)) - goto not_empty; - } else if (de->name[1] != '.') - goto not_empty; - } - de = nilfs_next_entry(de); - } - nilfs_put_page(page); - } - return 1; - -not_empty: - nilfs_put_page(page); - return 0; -} - -const struct file_operations nilfs_dir_operations = { - .llseek = generic_file_llseek, - .read = generic_read_dir, - .readdir = nilfs_readdir, - .unlocked_ioctl = nilfs_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = nilfs_compat_ioctl, -#endif /* CONFIG_COMPAT */ - .fsync = nilfs_sync_file, - -}; diff --git a/ANDROID_3.4.5/fs/nilfs2/direct.c b/ANDROID_3.4.5/fs/nilfs2/direct.c deleted file mode 100644 index 82f4865e..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/direct.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * direct.c - NILFS direct block pointer. - * - * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Koji Sato <koji@osrg.net>. - */ - -#include <linux/errno.h> -#include "nilfs.h" -#include "page.h" -#include "direct.h" -#include "alloc.h" -#include "dat.h" - -static inline __le64 *nilfs_direct_dptrs(const struct nilfs_bmap *direct) -{ - return (__le64 *) - ((struct nilfs_direct_node *)direct->b_u.u_data + 1); -} - -static inline __u64 -nilfs_direct_get_ptr(const struct nilfs_bmap *direct, __u64 key) -{ - return le64_to_cpu(*(nilfs_direct_dptrs(direct) + key)); -} - -static inline void nilfs_direct_set_ptr(struct nilfs_bmap *direct, - __u64 key, __u64 ptr) -{ - *(nilfs_direct_dptrs(direct) + key) = cpu_to_le64(ptr); -} - -static int nilfs_direct_lookup(const struct nilfs_bmap *direct, - __u64 key, int level, __u64 *ptrp) -{ - __u64 ptr; - - if (key > NILFS_DIRECT_KEY_MAX || level != 1) - return -ENOENT; - ptr = nilfs_direct_get_ptr(direct, key); - if (ptr == NILFS_BMAP_INVALID_PTR) - return -ENOENT; - - *ptrp = ptr; - return 0; -} - -static int nilfs_direct_lookup_contig(const struct nilfs_bmap *direct, - __u64 key, __u64 *ptrp, - unsigned maxblocks) -{ - struct inode *dat = NULL; - __u64 ptr, ptr2; - sector_t blocknr; - int ret, cnt; - - if (key > NILFS_DIRECT_KEY_MAX) - return -ENOENT; - ptr = nilfs_direct_get_ptr(direct, key); - if (ptr == NILFS_BMAP_INVALID_PTR) - return -ENOENT; - - if (NILFS_BMAP_USE_VBN(direct)) { - dat = nilfs_bmap_get_dat(direct); - ret = nilfs_dat_translate(dat, ptr, &blocknr); - if (ret < 0) - return ret; - ptr = blocknr; - } - - maxblocks = min_t(unsigned, maxblocks, NILFS_DIRECT_KEY_MAX - key + 1); - for (cnt = 1; cnt < maxblocks && - (ptr2 = nilfs_direct_get_ptr(direct, key + cnt)) != - NILFS_BMAP_INVALID_PTR; - cnt++) { - if (dat) { - ret = nilfs_dat_translate(dat, ptr2, &blocknr); - if (ret < 0) - return ret; - ptr2 = blocknr; - } - if (ptr2 != ptr + cnt) - break; - } - *ptrp = ptr; - return cnt; -} - -static __u64 -nilfs_direct_find_target_v(const struct nilfs_bmap *direct, __u64 key) -{ - __u64 ptr; - - ptr = nilfs_bmap_find_target_seq(direct, key); - if (ptr != NILFS_BMAP_INVALID_PTR) - /* sequential access */ - return ptr; - else - /* block group */ - return nilfs_bmap_find_target_in_group(direct); -} - -static int nilfs_direct_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr) -{ - union nilfs_bmap_ptr_req req; - struct inode *dat = NULL; - struct buffer_head *bh; - int ret; - - if (key > NILFS_DIRECT_KEY_MAX) - return -ENOENT; - if (nilfs_direct_get_ptr(bmap, key) != NILFS_BMAP_INVALID_PTR) - return -EEXIST; - - if (NILFS_BMAP_USE_VBN(bmap)) { - req.bpr_ptr = nilfs_direct_find_target_v(bmap, key); - dat = nilfs_bmap_get_dat(bmap); - } - ret = nilfs_bmap_prepare_alloc_ptr(bmap, &req, dat); - if (!ret) { - /* ptr must be a pointer to a buffer head. */ - bh = (struct buffer_head *)((unsigned long)ptr); - set_buffer_nilfs_volatile(bh); - - nilfs_bmap_commit_alloc_ptr(bmap, &req, dat); - nilfs_direct_set_ptr(bmap, key, req.bpr_ptr); - - if (!nilfs_bmap_dirty(bmap)) - nilfs_bmap_set_dirty(bmap); - - if (NILFS_BMAP_USE_VBN(bmap)) - nilfs_bmap_set_target_v(bmap, key, req.bpr_ptr); - - nilfs_inode_add_blocks(bmap->b_inode, 1); - } - return ret; -} - -static int nilfs_direct_delete(struct nilfs_bmap *bmap, __u64 key) -{ - union nilfs_bmap_ptr_req req; - struct inode *dat; - int ret; - - if (key > NILFS_DIRECT_KEY_MAX || - nilfs_direct_get_ptr(bmap, key) == NILFS_BMAP_INVALID_PTR) - return -ENOENT; - - dat = NILFS_BMAP_USE_VBN(bmap) ? nilfs_bmap_get_dat(bmap) : NULL; - req.bpr_ptr = nilfs_direct_get_ptr(bmap, key); - - ret = nilfs_bmap_prepare_end_ptr(bmap, &req, dat); - if (!ret) { - nilfs_bmap_commit_end_ptr(bmap, &req, dat); - nilfs_direct_set_ptr(bmap, key, NILFS_BMAP_INVALID_PTR); - nilfs_inode_sub_blocks(bmap->b_inode, 1); - } - return ret; -} - -static int nilfs_direct_last_key(const struct nilfs_bmap *direct, __u64 *keyp) -{ - __u64 key, lastkey; - - lastkey = NILFS_DIRECT_KEY_MAX + 1; - for (key = NILFS_DIRECT_KEY_MIN; key <= NILFS_DIRECT_KEY_MAX; key++) - if (nilfs_direct_get_ptr(direct, key) != - NILFS_BMAP_INVALID_PTR) - lastkey = key; - - if (lastkey == NILFS_DIRECT_KEY_MAX + 1) - return -ENOENT; - - *keyp = lastkey; - - return 0; -} - -static int nilfs_direct_check_insert(const struct nilfs_bmap *bmap, __u64 key) -{ - return key > NILFS_DIRECT_KEY_MAX; -} - -static int nilfs_direct_gather_data(struct nilfs_bmap *direct, - __u64 *keys, __u64 *ptrs, int nitems) -{ - __u64 key; - __u64 ptr; - int n; - - if (nitems > NILFS_DIRECT_NBLOCKS) - nitems = NILFS_DIRECT_NBLOCKS; - n = 0; - for (key = 0; key < nitems; key++) { - ptr = nilfs_direct_get_ptr(direct, key); - if (ptr != NILFS_BMAP_INVALID_PTR) { - keys[n] = key; - ptrs[n] = ptr; - n++; - } - } - return n; -} - -int nilfs_direct_delete_and_convert(struct nilfs_bmap *bmap, - __u64 key, __u64 *keys, __u64 *ptrs, int n) -{ - __le64 *dptrs; - int ret, i, j; - - /* no need to allocate any resource for conversion */ - - /* delete */ - ret = bmap->b_ops->bop_delete(bmap, key); - if (ret < 0) - return ret; - - /* free resources */ - if (bmap->b_ops->bop_clear != NULL) - bmap->b_ops->bop_clear(bmap); - - /* convert */ - dptrs = nilfs_direct_dptrs(bmap); - for (i = 0, j = 0; i < NILFS_DIRECT_NBLOCKS; i++) { - if ((j < n) && (i == keys[j])) { - dptrs[i] = (i != key) ? - cpu_to_le64(ptrs[j]) : - NILFS_BMAP_INVALID_PTR; - j++; - } else - dptrs[i] = NILFS_BMAP_INVALID_PTR; - } - - nilfs_direct_init(bmap); - return 0; -} - -static int nilfs_direct_propagate(struct nilfs_bmap *bmap, - struct buffer_head *bh) -{ - struct nilfs_palloc_req oldreq, newreq; - struct inode *dat; - __u64 key; - __u64 ptr; - int ret; - - if (!NILFS_BMAP_USE_VBN(bmap)) - return 0; - - dat = nilfs_bmap_get_dat(bmap); - key = nilfs_bmap_data_get_key(bmap, bh); - ptr = nilfs_direct_get_ptr(bmap, key); - if (!buffer_nilfs_volatile(bh)) { - oldreq.pr_entry_nr = ptr; - newreq.pr_entry_nr = ptr; - ret = nilfs_dat_prepare_update(dat, &oldreq, &newreq); - if (ret < 0) - return ret; - nilfs_dat_commit_update(dat, &oldreq, &newreq, - bmap->b_ptr_type == NILFS_BMAP_PTR_VS); - set_buffer_nilfs_volatile(bh); - nilfs_direct_set_ptr(bmap, key, newreq.pr_entry_nr); - } else - ret = nilfs_dat_mark_dirty(dat, ptr); - - return ret; -} - -static int nilfs_direct_assign_v(struct nilfs_bmap *direct, - __u64 key, __u64 ptr, - struct buffer_head **bh, - sector_t blocknr, - union nilfs_binfo *binfo) -{ - struct inode *dat = nilfs_bmap_get_dat(direct); - union nilfs_bmap_ptr_req req; - int ret; - - req.bpr_ptr = ptr; - ret = nilfs_dat_prepare_start(dat, &req.bpr_req); - if (!ret) { - nilfs_dat_commit_start(dat, &req.bpr_req, blocknr); - binfo->bi_v.bi_vblocknr = cpu_to_le64(ptr); - binfo->bi_v.bi_blkoff = cpu_to_le64(key); - } - return ret; -} - -static int nilfs_direct_assign_p(struct nilfs_bmap *direct, - __u64 key, __u64 ptr, - struct buffer_head **bh, - sector_t blocknr, - union nilfs_binfo *binfo) -{ - nilfs_direct_set_ptr(direct, key, blocknr); - - binfo->bi_dat.bi_blkoff = cpu_to_le64(key); - binfo->bi_dat.bi_level = 0; - - return 0; -} - -static int nilfs_direct_assign(struct nilfs_bmap *bmap, - struct buffer_head **bh, - sector_t blocknr, - union nilfs_binfo *binfo) -{ - __u64 key; - __u64 ptr; - - key = nilfs_bmap_data_get_key(bmap, *bh); - if (unlikely(key > NILFS_DIRECT_KEY_MAX)) { - printk(KERN_CRIT "%s: invalid key: %llu\n", __func__, - (unsigned long long)key); - return -EINVAL; - } - ptr = nilfs_direct_get_ptr(bmap, key); - if (unlikely(ptr == NILFS_BMAP_INVALID_PTR)) { - printk(KERN_CRIT "%s: invalid pointer: %llu\n", __func__, - (unsigned long long)ptr); - return -EINVAL; - } - - return NILFS_BMAP_USE_VBN(bmap) ? - nilfs_direct_assign_v(bmap, key, ptr, bh, blocknr, binfo) : - nilfs_direct_assign_p(bmap, key, ptr, bh, blocknr, binfo); -} - -static const struct nilfs_bmap_operations nilfs_direct_ops = { - .bop_lookup = nilfs_direct_lookup, - .bop_lookup_contig = nilfs_direct_lookup_contig, - .bop_insert = nilfs_direct_insert, - .bop_delete = nilfs_direct_delete, - .bop_clear = NULL, - - .bop_propagate = nilfs_direct_propagate, - - .bop_lookup_dirty_buffers = NULL, - - .bop_assign = nilfs_direct_assign, - .bop_mark = NULL, - - .bop_last_key = nilfs_direct_last_key, - .bop_check_insert = nilfs_direct_check_insert, - .bop_check_delete = NULL, - .bop_gather_data = nilfs_direct_gather_data, -}; - - -int nilfs_direct_init(struct nilfs_bmap *bmap) -{ - bmap->b_ops = &nilfs_direct_ops; - return 0; -} diff --git a/ANDROID_3.4.5/fs/nilfs2/direct.h b/ANDROID_3.4.5/fs/nilfs2/direct.h deleted file mode 100644 index dc643de2..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/direct.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * direct.h - NILFS direct block pointer. - * - * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Koji Sato <koji@osrg.net>. - */ - -#ifndef _NILFS_DIRECT_H -#define _NILFS_DIRECT_H - -#include <linux/types.h> -#include <linux/buffer_head.h> -#include "bmap.h" - - -/** - * struct nilfs_direct_node - direct node - * @dn_flags: flags - * @dn_pad: padding - */ -struct nilfs_direct_node { - __u8 dn_flags; - __u8 pad[7]; -}; - -#define NILFS_DIRECT_NBLOCKS (NILFS_BMAP_SIZE / sizeof(__le64) - 1) -#define NILFS_DIRECT_KEY_MIN 0 -#define NILFS_DIRECT_KEY_MAX (NILFS_DIRECT_NBLOCKS - 1) - - -int nilfs_direct_init(struct nilfs_bmap *); -int nilfs_direct_delete_and_convert(struct nilfs_bmap *, __u64, __u64 *, - __u64 *, int); - - -#endif /* _NILFS_DIRECT_H */ diff --git a/ANDROID_3.4.5/fs/nilfs2/export.h b/ANDROID_3.4.5/fs/nilfs2/export.h deleted file mode 100644 index a71cc412..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/export.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef NILFS_EXPORT_H -#define NILFS_EXPORT_H - -#include <linux/exportfs.h> - -extern const struct export_operations nilfs_export_ops; - -struct nilfs_fid { - u64 cno; - u64 ino; - u32 gen; - - u32 parent_gen; - u64 parent_ino; -} __attribute__ ((packed)); - -#endif diff --git a/ANDROID_3.4.5/fs/nilfs2/file.c b/ANDROID_3.4.5/fs/nilfs2/file.c deleted file mode 100644 index 26601529..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/file.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * file.c - NILFS regular file handling primitives including fsync(). - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Amagai Yoshiji <amagai@osrg.net>, - * Ryusuke Konishi <ryusuke@osrg.net> - */ - -#include <linux/fs.h> -#include <linux/mm.h> -#include <linux/writeback.h> -#include "nilfs.h" -#include "segment.h" - -int nilfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) -{ - /* - * Called from fsync() system call - * This is the only entry point that can catch write and synch - * timing for both data blocks and intermediate blocks. - * - * This function should be implemented when the writeback function - * will be implemented. - */ - struct inode *inode = file->f_mapping->host; - int err; - - err = filemap_write_and_wait_range(inode->i_mapping, start, end); - if (err) - return err; - mutex_lock(&inode->i_mutex); - - if (!nilfs_inode_dirty(inode)) { - mutex_unlock(&inode->i_mutex); - return 0; - } - - if (datasync) - err = nilfs_construct_dsync_segment(inode->i_sb, inode, 0, - LLONG_MAX); - else - err = nilfs_construct_segment(inode->i_sb); - - mutex_unlock(&inode->i_mutex); - return err; -} - -static int nilfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - struct page *page = vmf->page; - struct inode *inode = vma->vm_file->f_dentry->d_inode; - struct nilfs_transaction_info ti; - int ret; - - if (unlikely(nilfs_near_disk_full(inode->i_sb->s_fs_info))) - return VM_FAULT_SIGBUS; /* -ENOSPC */ - - lock_page(page); - if (page->mapping != inode->i_mapping || - page_offset(page) >= i_size_read(inode) || !PageUptodate(page)) { - unlock_page(page); - return VM_FAULT_NOPAGE; /* make the VM retry the fault */ - } - - /* - * check to see if the page is mapped already (no holes) - */ - if (PageMappedToDisk(page)) - goto mapped; - - if (page_has_buffers(page)) { - struct buffer_head *bh, *head; - int fully_mapped = 1; - - bh = head = page_buffers(page); - do { - if (!buffer_mapped(bh)) { - fully_mapped = 0; - break; - } - } while (bh = bh->b_this_page, bh != head); - - if (fully_mapped) { - SetPageMappedToDisk(page); - goto mapped; - } - } - unlock_page(page); - - /* - * fill hole blocks - */ - ret = nilfs_transaction_begin(inode->i_sb, &ti, 1); - /* never returns -ENOMEM, but may return -ENOSPC */ - if (unlikely(ret)) - return VM_FAULT_SIGBUS; - - ret = block_page_mkwrite(vma, vmf, nilfs_get_block); - if (ret != VM_FAULT_LOCKED) { - nilfs_transaction_abort(inode->i_sb); - return ret; - } - nilfs_set_file_dirty(inode, 1 << (PAGE_SHIFT - inode->i_blkbits)); - nilfs_transaction_commit(inode->i_sb); - - mapped: - wait_on_page_writeback(page); - return VM_FAULT_LOCKED; -} - -static const struct vm_operations_struct nilfs_file_vm_ops = { - .fault = filemap_fault, - .page_mkwrite = nilfs_page_mkwrite, -}; - -static int nilfs_file_mmap(struct file *file, struct vm_area_struct *vma) -{ - file_accessed(file); - vma->vm_ops = &nilfs_file_vm_ops; - vma->vm_flags |= VM_CAN_NONLINEAR; - return 0; -} - -/* - * We have mostly NULL's here: the current defaults are ok for - * the nilfs filesystem. - */ -const struct file_operations nilfs_file_operations = { - .llseek = generic_file_llseek, - .read = do_sync_read, - .write = do_sync_write, - .aio_read = generic_file_aio_read, - .aio_write = generic_file_aio_write, - .unlocked_ioctl = nilfs_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = nilfs_compat_ioctl, -#endif /* CONFIG_COMPAT */ - .mmap = nilfs_file_mmap, - .open = generic_file_open, - /* .release = nilfs_release_file, */ - .fsync = nilfs_sync_file, - .splice_read = generic_file_splice_read, -}; - -const struct inode_operations nilfs_file_inode_operations = { - .truncate = nilfs_truncate, - .setattr = nilfs_setattr, - .permission = nilfs_permission, - .fiemap = nilfs_fiemap, -}; - -/* end of file */ diff --git a/ANDROID_3.4.5/fs/nilfs2/gcinode.c b/ANDROID_3.4.5/fs/nilfs2/gcinode.c deleted file mode 100644 index 57ceaf33..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/gcinode.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * gcinode.c - dummy inodes to buffer blocks for garbage collection - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Seiji Kihara <kihara@osrg.net>, Amagai Yoshiji <amagai@osrg.net>, - * and Ryusuke Konishi <ryusuke@osrg.net>. - * Revised by Ryusuke Konishi <ryusuke@osrg.net>. - * - */ -/* - * This file adds the cache of on-disk blocks to be moved in garbage - * collection. The disk blocks are held with dummy inodes (called - * gcinodes), and this file provides lookup function of the dummy - * inodes and their buffer read function. - * - * Buffers and pages held by the dummy inodes will be released each - * time after they are copied to a new log. Dirty blocks made on the - * current generation and the blocks to be moved by GC never overlap - * because the dirty blocks make a new generation; they rather must be - * written individually. - */ - -#include <linux/buffer_head.h> -#include <linux/mpage.h> -#include <linux/hash.h> -#include <linux/slab.h> -#include <linux/swap.h> -#include "nilfs.h" -#include "btree.h" -#include "btnode.h" -#include "page.h" -#include "mdt.h" -#include "dat.h" -#include "ifile.h" - -/* - * nilfs_gccache_submit_read_data() - add data buffer and submit read request - * @inode - gc inode - * @blkoff - dummy offset treated as the key for the page cache - * @pbn - physical block number of the block - * @vbn - virtual block number of the block, 0 for non-virtual block - * @out_bh - indirect pointer to a buffer_head struct to receive the results - * - * Description: nilfs_gccache_submit_read_data() registers the data buffer - * specified by @pbn to the GC pagecache with the key @blkoff. - * This function sets @vbn (@pbn if @vbn is zero) in b_blocknr of the buffer. - * - * Return Value: On success, 0 is returned. On Error, one of the following - * negative error code is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - * - * %-ENOENT - The block specified with @pbn does not exist. - */ -int nilfs_gccache_submit_read_data(struct inode *inode, sector_t blkoff, - sector_t pbn, __u64 vbn, - struct buffer_head **out_bh) -{ - struct buffer_head *bh; - int err; - - bh = nilfs_grab_buffer(inode, inode->i_mapping, blkoff, 0); - if (unlikely(!bh)) - return -ENOMEM; - - if (buffer_uptodate(bh)) - goto out; - - if (pbn == 0) { - struct the_nilfs *nilfs = inode->i_sb->s_fs_info; - - err = nilfs_dat_translate(nilfs->ns_dat, vbn, &pbn); - if (unlikely(err)) { /* -EIO, -ENOMEM, -ENOENT */ - brelse(bh); - goto failed; - } - } - - lock_buffer(bh); - if (buffer_uptodate(bh)) { - unlock_buffer(bh); - goto out; - } - - if (!buffer_mapped(bh)) { - bh->b_bdev = inode->i_sb->s_bdev; - set_buffer_mapped(bh); - } - bh->b_blocknr = pbn; - bh->b_end_io = end_buffer_read_sync; - get_bh(bh); - submit_bh(READ, bh); - if (vbn) - bh->b_blocknr = vbn; - out: - err = 0; - *out_bh = bh; - - failed: - unlock_page(bh->b_page); - page_cache_release(bh->b_page); - return err; -} - -/* - * nilfs_gccache_submit_read_node() - add node buffer and submit read request - * @inode - gc inode - * @pbn - physical block number for the block - * @vbn - virtual block number for the block - * @out_bh - indirect pointer to a buffer_head struct to receive the results - * - * Description: nilfs_gccache_submit_read_node() registers the node buffer - * specified by @vbn to the GC pagecache. @pbn can be supplied by the - * caller to avoid translation of the disk block address. - * - * Return Value: On success, 0 is returned. On Error, one of the following - * negative error code is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - */ -int nilfs_gccache_submit_read_node(struct inode *inode, sector_t pbn, - __u64 vbn, struct buffer_head **out_bh) -{ - int ret; - - ret = nilfs_btnode_submit_block(&NILFS_I(inode)->i_btnode_cache, - vbn ? : pbn, pbn, READ, out_bh, &pbn); - if (ret == -EEXIST) /* internal code (cache hit) */ - ret = 0; - return ret; -} - -int nilfs_gccache_wait_and_mark_dirty(struct buffer_head *bh) -{ - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) - return -EIO; - if (buffer_dirty(bh)) - return -EEXIST; - - if (buffer_nilfs_node(bh) && nilfs_btree_broken_node_block(bh)) { - clear_buffer_uptodate(bh); - return -EIO; - } - mark_buffer_dirty(bh); - return 0; -} - -int nilfs_init_gcinode(struct inode *inode) -{ - struct nilfs_inode_info *ii = NILFS_I(inode); - - inode->i_mode = S_IFREG; - mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS); - inode->i_mapping->a_ops = &empty_aops; - inode->i_mapping->backing_dev_info = inode->i_sb->s_bdi; - - ii->i_flags = 0; - nilfs_bmap_init_gc(ii->i_bmap); - - return 0; -} - -/** - * nilfs_remove_all_gcinodes() - remove all unprocessed gc inodes - */ -void nilfs_remove_all_gcinodes(struct the_nilfs *nilfs) -{ - struct list_head *head = &nilfs->ns_gc_inodes; - struct nilfs_inode_info *ii; - - while (!list_empty(head)) { - ii = list_first_entry(head, struct nilfs_inode_info, i_dirty); - list_del_init(&ii->i_dirty); - truncate_inode_pages(&ii->vfs_inode.i_data, 0); - nilfs_btnode_cache_clear(&ii->i_btnode_cache); - iput(&ii->vfs_inode); - } -} diff --git a/ANDROID_3.4.5/fs/nilfs2/ifile.c b/ANDROID_3.4.5/fs/nilfs2/ifile.c deleted file mode 100644 index 5a48df79..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/ifile.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * ifile.c - NILFS inode file - * - * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Amagai Yoshiji <amagai@osrg.net>. - * Revised by Ryusuke Konishi <ryusuke@osrg.net>. - * - */ - -#include <linux/types.h> -#include <linux/buffer_head.h> -#include "nilfs.h" -#include "mdt.h" -#include "alloc.h" -#include "ifile.h" - - -struct nilfs_ifile_info { - struct nilfs_mdt_info mi; - struct nilfs_palloc_cache palloc_cache; -}; - -static inline struct nilfs_ifile_info *NILFS_IFILE_I(struct inode *ifile) -{ - return (struct nilfs_ifile_info *)NILFS_MDT(ifile); -} - -/** - * nilfs_ifile_create_inode - create a new disk inode - * @ifile: ifile inode - * @out_ino: pointer to a variable to store inode number - * @out_bh: buffer_head contains newly allocated disk inode - * - * Return Value: On success, 0 is returned and the newly allocated inode - * number is stored in the place pointed by @ino, and buffer_head pointer - * that contains newly allocated disk inode structure is stored in the - * place pointed by @out_bh - * On error, one of the following negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - * - * %-ENOSPC - No inode left. - */ -int nilfs_ifile_create_inode(struct inode *ifile, ino_t *out_ino, - struct buffer_head **out_bh) -{ - struct nilfs_palloc_req req; - int ret; - - req.pr_entry_nr = 0; /* 0 says find free inode from beginning of - a group. dull code!! */ - req.pr_entry_bh = NULL; - - ret = nilfs_palloc_prepare_alloc_entry(ifile, &req); - if (!ret) { - ret = nilfs_palloc_get_entry_block(ifile, req.pr_entry_nr, 1, - &req.pr_entry_bh); - if (ret < 0) - nilfs_palloc_abort_alloc_entry(ifile, &req); - } - if (ret < 0) { - brelse(req.pr_entry_bh); - return ret; - } - nilfs_palloc_commit_alloc_entry(ifile, &req); - mark_buffer_dirty(req.pr_entry_bh); - nilfs_mdt_mark_dirty(ifile); - *out_ino = (ino_t)req.pr_entry_nr; - *out_bh = req.pr_entry_bh; - return 0; -} - -/** - * nilfs_ifile_delete_inode - delete a disk inode - * @ifile: ifile inode - * @ino: inode number - * - * Return Value: On success, 0 is returned. On error, one of the following - * negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - * - * %-ENOENT - The inode number @ino have not been allocated. - */ -int nilfs_ifile_delete_inode(struct inode *ifile, ino_t ino) -{ - struct nilfs_palloc_req req = { - .pr_entry_nr = ino, .pr_entry_bh = NULL - }; - struct nilfs_inode *raw_inode; - void *kaddr; - int ret; - - ret = nilfs_palloc_prepare_free_entry(ifile, &req); - if (!ret) { - ret = nilfs_palloc_get_entry_block(ifile, req.pr_entry_nr, 0, - &req.pr_entry_bh); - if (ret < 0) - nilfs_palloc_abort_free_entry(ifile, &req); - } - if (ret < 0) { - brelse(req.pr_entry_bh); - return ret; - } - - kaddr = kmap_atomic(req.pr_entry_bh->b_page); - raw_inode = nilfs_palloc_block_get_entry(ifile, req.pr_entry_nr, - req.pr_entry_bh, kaddr); - raw_inode->i_flags = 0; - kunmap_atomic(kaddr); - - mark_buffer_dirty(req.pr_entry_bh); - brelse(req.pr_entry_bh); - - nilfs_palloc_commit_free_entry(ifile, &req); - - return 0; -} - -int nilfs_ifile_get_inode_block(struct inode *ifile, ino_t ino, - struct buffer_head **out_bh) -{ - struct super_block *sb = ifile->i_sb; - int err; - - if (unlikely(!NILFS_VALID_INODE(sb, ino))) { - nilfs_error(sb, __func__, "bad inode number: %lu", - (unsigned long) ino); - return -EINVAL; - } - - err = nilfs_palloc_get_entry_block(ifile, ino, 0, out_bh); - if (unlikely(err)) - nilfs_warning(sb, __func__, "unable to read inode: %lu", - (unsigned long) ino); - return err; -} - -/** - * nilfs_ifile_read - read or get ifile inode - * @sb: super block instance - * @root: root object - * @inode_size: size of an inode - * @raw_inode: on-disk ifile inode - * @inodep: buffer to store the inode - */ -int nilfs_ifile_read(struct super_block *sb, struct nilfs_root *root, - size_t inode_size, struct nilfs_inode *raw_inode, - struct inode **inodep) -{ - struct inode *ifile; - int err; - - ifile = nilfs_iget_locked(sb, root, NILFS_IFILE_INO); - if (unlikely(!ifile)) - return -ENOMEM; - if (!(ifile->i_state & I_NEW)) - goto out; - - err = nilfs_mdt_init(ifile, NILFS_MDT_GFP, - sizeof(struct nilfs_ifile_info)); - if (err) - goto failed; - - err = nilfs_palloc_init_blockgroup(ifile, inode_size); - if (err) - goto failed; - - nilfs_palloc_setup_cache(ifile, &NILFS_IFILE_I(ifile)->palloc_cache); - - err = nilfs_read_inode_common(ifile, raw_inode); - if (err) - goto failed; - - unlock_new_inode(ifile); - out: - *inodep = ifile; - return 0; - failed: - iget_failed(ifile); - return err; -} diff --git a/ANDROID_3.4.5/fs/nilfs2/ifile.h b/ANDROID_3.4.5/fs/nilfs2/ifile.h deleted file mode 100644 index 59b6f2b5..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/ifile.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * ifile.h - NILFS inode file - * - * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Amagai Yoshiji <amagai@osrg.net> - * Revised by Ryusuke Konishi <ryusuke@osrg.net> - * - */ - -#ifndef _NILFS_IFILE_H -#define _NILFS_IFILE_H - -#include <linux/fs.h> -#include <linux/buffer_head.h> -#include <linux/nilfs2_fs.h> -#include "mdt.h" -#include "alloc.h" - - -static inline struct nilfs_inode * -nilfs_ifile_map_inode(struct inode *ifile, ino_t ino, struct buffer_head *ibh) -{ - void *kaddr = kmap(ibh->b_page); - return nilfs_palloc_block_get_entry(ifile, ino, ibh, kaddr); -} - -static inline void nilfs_ifile_unmap_inode(struct inode *ifile, ino_t ino, - struct buffer_head *ibh) -{ - kunmap(ibh->b_page); -} - -int nilfs_ifile_create_inode(struct inode *, ino_t *, struct buffer_head **); -int nilfs_ifile_delete_inode(struct inode *, ino_t); -int nilfs_ifile_get_inode_block(struct inode *, ino_t, struct buffer_head **); - -int nilfs_ifile_read(struct super_block *sb, struct nilfs_root *root, - size_t inode_size, struct nilfs_inode *raw_inode, - struct inode **inodep); - -#endif /* _NILFS_IFILE_H */ diff --git a/ANDROID_3.4.5/fs/nilfs2/inode.c b/ANDROID_3.4.5/fs/nilfs2/inode.c deleted file mode 100644 index 8f7b95ac..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/inode.c +++ /dev/null @@ -1,1066 +0,0 @@ -/* - * inode.c - NILFS inode operations. - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Ryusuke Konishi <ryusuke@osrg.net> - * - */ - -#include <linux/buffer_head.h> -#include <linux/gfp.h> -#include <linux/mpage.h> -#include <linux/writeback.h> -#include <linux/uio.h> -#include "nilfs.h" -#include "btnode.h" -#include "segment.h" -#include "page.h" -#include "mdt.h" -#include "cpfile.h" -#include "ifile.h" - -struct nilfs_iget_args { - u64 ino; - __u64 cno; - struct nilfs_root *root; - int for_gc; -}; - -void nilfs_inode_add_blocks(struct inode *inode, int n) -{ - struct nilfs_root *root = NILFS_I(inode)->i_root; - - inode_add_bytes(inode, (1 << inode->i_blkbits) * n); - if (root) - atomic_add(n, &root->blocks_count); -} - -void nilfs_inode_sub_blocks(struct inode *inode, int n) -{ - struct nilfs_root *root = NILFS_I(inode)->i_root; - - inode_sub_bytes(inode, (1 << inode->i_blkbits) * n); - if (root) - atomic_sub(n, &root->blocks_count); -} - -/** - * nilfs_get_block() - get a file block on the filesystem (callback function) - * @inode - inode struct of the target file - * @blkoff - file block number - * @bh_result - buffer head to be mapped on - * @create - indicate whether allocating the block or not when it has not - * been allocated yet. - * - * This function does not issue actual read request of the specified data - * block. It is done by VFS. - */ -int nilfs_get_block(struct inode *inode, sector_t blkoff, - struct buffer_head *bh_result, int create) -{ - struct nilfs_inode_info *ii = NILFS_I(inode); - struct the_nilfs *nilfs = inode->i_sb->s_fs_info; - __u64 blknum = 0; - int err = 0, ret; - unsigned maxblocks = bh_result->b_size >> inode->i_blkbits; - - down_read(&NILFS_MDT(nilfs->ns_dat)->mi_sem); - ret = nilfs_bmap_lookup_contig(ii->i_bmap, blkoff, &blknum, maxblocks); - up_read(&NILFS_MDT(nilfs->ns_dat)->mi_sem); - if (ret >= 0) { /* found */ - map_bh(bh_result, inode->i_sb, blknum); - if (ret > 0) - bh_result->b_size = (ret << inode->i_blkbits); - goto out; - } - /* data block was not found */ - if (ret == -ENOENT && create) { - struct nilfs_transaction_info ti; - - bh_result->b_blocknr = 0; - err = nilfs_transaction_begin(inode->i_sb, &ti, 1); - if (unlikely(err)) - goto out; - err = nilfs_bmap_insert(ii->i_bmap, (unsigned long)blkoff, - (unsigned long)bh_result); - if (unlikely(err != 0)) { - if (err == -EEXIST) { - /* - * The get_block() function could be called - * from multiple callers for an inode. - * However, the page having this block must - * be locked in this case. - */ - printk(KERN_WARNING - "nilfs_get_block: a race condition " - "while inserting a data block. " - "(inode number=%lu, file block " - "offset=%llu)\n", - inode->i_ino, - (unsigned long long)blkoff); - err = 0; - } - nilfs_transaction_abort(inode->i_sb); - goto out; - } - nilfs_mark_inode_dirty(inode); - nilfs_transaction_commit(inode->i_sb); /* never fails */ - /* Error handling should be detailed */ - set_buffer_new(bh_result); - set_buffer_delay(bh_result); - map_bh(bh_result, inode->i_sb, 0); /* dbn must be changed - to proper value */ - } else if (ret == -ENOENT) { - /* not found is not error (e.g. hole); must return without - the mapped state flag. */ - ; - } else { - err = ret; - } - - out: - return err; -} - -/** - * nilfs_readpage() - implement readpage() method of nilfs_aops {} - * address_space_operations. - * @file - file struct of the file to be read - * @page - the page to be read - */ -static int nilfs_readpage(struct file *file, struct page *page) -{ - return mpage_readpage(page, nilfs_get_block); -} - -/** - * nilfs_readpages() - implement readpages() method of nilfs_aops {} - * address_space_operations. - * @file - file struct of the file to be read - * @mapping - address_space struct used for reading multiple pages - * @pages - the pages to be read - * @nr_pages - number of pages to be read - */ -static int nilfs_readpages(struct file *file, struct address_space *mapping, - struct list_head *pages, unsigned nr_pages) -{ - return mpage_readpages(mapping, pages, nr_pages, nilfs_get_block); -} - -static int nilfs_writepages(struct address_space *mapping, - struct writeback_control *wbc) -{ - struct inode *inode = mapping->host; - int err = 0; - - if (wbc->sync_mode == WB_SYNC_ALL) - err = nilfs_construct_dsync_segment(inode->i_sb, inode, - wbc->range_start, - wbc->range_end); - return err; -} - -static int nilfs_writepage(struct page *page, struct writeback_control *wbc) -{ - struct inode *inode = page->mapping->host; - int err; - - redirty_page_for_writepage(wbc, page); - unlock_page(page); - - if (wbc->sync_mode == WB_SYNC_ALL) { - err = nilfs_construct_segment(inode->i_sb); - if (unlikely(err)) - return err; - } else if (wbc->for_reclaim) - nilfs_flush_segment(inode->i_sb, inode->i_ino); - - return 0; -} - -static int nilfs_set_page_dirty(struct page *page) -{ - int ret = __set_page_dirty_buffers(page); - - if (ret) { - struct inode *inode = page->mapping->host; - unsigned nr_dirty = 1 << (PAGE_SHIFT - inode->i_blkbits); - - nilfs_set_file_dirty(inode, nr_dirty); - } - return ret; -} - -static int nilfs_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata) - -{ - struct inode *inode = mapping->host; - int err = nilfs_transaction_begin(inode->i_sb, NULL, 1); - - if (unlikely(err)) - return err; - - err = block_write_begin(mapping, pos, len, flags, pagep, - nilfs_get_block); - if (unlikely(err)) { - loff_t isize = mapping->host->i_size; - if (pos + len > isize) - vmtruncate(mapping->host, isize); - - nilfs_transaction_abort(inode->i_sb); - } - return err; -} - -static int nilfs_write_end(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned copied, - struct page *page, void *fsdata) -{ - struct inode *inode = mapping->host; - unsigned start = pos & (PAGE_CACHE_SIZE - 1); - unsigned nr_dirty; - int err; - - nr_dirty = nilfs_page_count_clean_buffers(page, start, - start + copied); - copied = generic_write_end(file, mapping, pos, len, copied, page, - fsdata); - nilfs_set_file_dirty(inode, nr_dirty); - err = nilfs_transaction_commit(inode->i_sb); - return err ? : copied; -} - -static ssize_t -nilfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, - loff_t offset, unsigned long nr_segs) -{ - struct file *file = iocb->ki_filp; - struct inode *inode = file->f_mapping->host; - ssize_t size; - - if (rw == WRITE) - return 0; - - /* Needs synchronization with the cleaner */ - size = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs, - nilfs_get_block); - - /* - * In case of error extending write may have instantiated a few - * blocks outside i_size. Trim these off again. - */ - if (unlikely((rw & WRITE) && size < 0)) { - loff_t isize = i_size_read(inode); - loff_t end = offset + iov_length(iov, nr_segs); - - if (end > isize) - vmtruncate(inode, isize); - } - - return size; -} - -const struct address_space_operations nilfs_aops = { - .writepage = nilfs_writepage, - .readpage = nilfs_readpage, - .writepages = nilfs_writepages, - .set_page_dirty = nilfs_set_page_dirty, - .readpages = nilfs_readpages, - .write_begin = nilfs_write_begin, - .write_end = nilfs_write_end, - /* .releasepage = nilfs_releasepage, */ - .invalidatepage = block_invalidatepage, - .direct_IO = nilfs_direct_IO, - .is_partially_uptodate = block_is_partially_uptodate, -}; - -struct inode *nilfs_new_inode(struct inode *dir, umode_t mode) -{ - struct super_block *sb = dir->i_sb; - struct the_nilfs *nilfs = sb->s_fs_info; - struct inode *inode; - struct nilfs_inode_info *ii; - struct nilfs_root *root; - int err = -ENOMEM; - ino_t ino; - - inode = new_inode(sb); - if (unlikely(!inode)) - goto failed; - - mapping_set_gfp_mask(inode->i_mapping, - mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS); - - root = NILFS_I(dir)->i_root; - ii = NILFS_I(inode); - ii->i_state = 1 << NILFS_I_NEW; - ii->i_root = root; - - err = nilfs_ifile_create_inode(root->ifile, &ino, &ii->i_bh); - if (unlikely(err)) - goto failed_ifile_create_inode; - /* reference count of i_bh inherits from nilfs_mdt_read_block() */ - - atomic_inc(&root->inodes_count); - inode_init_owner(inode, dir, mode); - inode->i_ino = ino; - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - - if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) { - err = nilfs_bmap_read(ii->i_bmap, NULL); - if (err < 0) - goto failed_bmap; - - set_bit(NILFS_I_BMAP, &ii->i_state); - /* No lock is needed; iget() ensures it. */ - } - - ii->i_flags = nilfs_mask_flags( - mode, NILFS_I(dir)->i_flags & NILFS_FL_INHERITED); - - /* ii->i_file_acl = 0; */ - /* ii->i_dir_acl = 0; */ - ii->i_dir_start_lookup = 0; - nilfs_set_inode_flags(inode); - spin_lock(&nilfs->ns_next_gen_lock); - inode->i_generation = nilfs->ns_next_generation++; - spin_unlock(&nilfs->ns_next_gen_lock); - insert_inode_hash(inode); - - err = nilfs_init_acl(inode, dir); - if (unlikely(err)) - goto failed_acl; /* never occur. When supporting - nilfs_init_acl(), proper cancellation of - above jobs should be considered */ - - return inode; - - failed_acl: - failed_bmap: - clear_nlink(inode); - iput(inode); /* raw_inode will be deleted through - generic_delete_inode() */ - goto failed; - - failed_ifile_create_inode: - make_bad_inode(inode); - iput(inode); /* if i_nlink == 1, generic_forget_inode() will be - called */ - failed: - return ERR_PTR(err); -} - -void nilfs_set_inode_flags(struct inode *inode) -{ - unsigned int flags = NILFS_I(inode)->i_flags; - - inode->i_flags &= ~(S_SYNC | S_APPEND | S_IMMUTABLE | S_NOATIME | - S_DIRSYNC); - if (flags & FS_SYNC_FL) - inode->i_flags |= S_SYNC; - if (flags & FS_APPEND_FL) - inode->i_flags |= S_APPEND; - if (flags & FS_IMMUTABLE_FL) - inode->i_flags |= S_IMMUTABLE; - if (flags & FS_NOATIME_FL) - inode->i_flags |= S_NOATIME; - if (flags & FS_DIRSYNC_FL) - inode->i_flags |= S_DIRSYNC; - mapping_set_gfp_mask(inode->i_mapping, - mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS); -} - -int nilfs_read_inode_common(struct inode *inode, - struct nilfs_inode *raw_inode) -{ - struct nilfs_inode_info *ii = NILFS_I(inode); - int err; - - inode->i_mode = le16_to_cpu(raw_inode->i_mode); - inode->i_uid = (uid_t)le32_to_cpu(raw_inode->i_uid); - inode->i_gid = (gid_t)le32_to_cpu(raw_inode->i_gid); - set_nlink(inode, le16_to_cpu(raw_inode->i_links_count)); - inode->i_size = le64_to_cpu(raw_inode->i_size); - inode->i_atime.tv_sec = le64_to_cpu(raw_inode->i_mtime); - inode->i_ctime.tv_sec = le64_to_cpu(raw_inode->i_ctime); - inode->i_mtime.tv_sec = le64_to_cpu(raw_inode->i_mtime); - inode->i_atime.tv_nsec = le32_to_cpu(raw_inode->i_mtime_nsec); - inode->i_ctime.tv_nsec = le32_to_cpu(raw_inode->i_ctime_nsec); - inode->i_mtime.tv_nsec = le32_to_cpu(raw_inode->i_mtime_nsec); - if (inode->i_nlink == 0 && inode->i_mode == 0) - return -EINVAL; /* this inode is deleted */ - - inode->i_blocks = le64_to_cpu(raw_inode->i_blocks); - ii->i_flags = le32_to_cpu(raw_inode->i_flags); -#if 0 - ii->i_file_acl = le32_to_cpu(raw_inode->i_file_acl); - ii->i_dir_acl = S_ISREG(inode->i_mode) ? - 0 : le32_to_cpu(raw_inode->i_dir_acl); -#endif - ii->i_dir_start_lookup = 0; - inode->i_generation = le32_to_cpu(raw_inode->i_generation); - - if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || - S_ISLNK(inode->i_mode)) { - err = nilfs_bmap_read(ii->i_bmap, raw_inode); - if (err < 0) - return err; - set_bit(NILFS_I_BMAP, &ii->i_state); - /* No lock is needed; iget() ensures it. */ - } - return 0; -} - -static int __nilfs_read_inode(struct super_block *sb, - struct nilfs_root *root, unsigned long ino, - struct inode *inode) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct buffer_head *bh; - struct nilfs_inode *raw_inode; - int err; - - down_read(&NILFS_MDT(nilfs->ns_dat)->mi_sem); - err = nilfs_ifile_get_inode_block(root->ifile, ino, &bh); - if (unlikely(err)) - goto bad_inode; - - raw_inode = nilfs_ifile_map_inode(root->ifile, ino, bh); - - err = nilfs_read_inode_common(inode, raw_inode); - if (err) - goto failed_unmap; - - if (S_ISREG(inode->i_mode)) { - inode->i_op = &nilfs_file_inode_operations; - inode->i_fop = &nilfs_file_operations; - inode->i_mapping->a_ops = &nilfs_aops; - } else if (S_ISDIR(inode->i_mode)) { - inode->i_op = &nilfs_dir_inode_operations; - inode->i_fop = &nilfs_dir_operations; - inode->i_mapping->a_ops = &nilfs_aops; - } else if (S_ISLNK(inode->i_mode)) { - inode->i_op = &nilfs_symlink_inode_operations; - inode->i_mapping->a_ops = &nilfs_aops; - } else { - inode->i_op = &nilfs_special_inode_operations; - init_special_inode( - inode, inode->i_mode, - huge_decode_dev(le64_to_cpu(raw_inode->i_device_code))); - } - nilfs_ifile_unmap_inode(root->ifile, ino, bh); - brelse(bh); - up_read(&NILFS_MDT(nilfs->ns_dat)->mi_sem); - nilfs_set_inode_flags(inode); - return 0; - - failed_unmap: - nilfs_ifile_unmap_inode(root->ifile, ino, bh); - brelse(bh); - - bad_inode: - up_read(&NILFS_MDT(nilfs->ns_dat)->mi_sem); - return err; -} - -static int nilfs_iget_test(struct inode *inode, void *opaque) -{ - struct nilfs_iget_args *args = opaque; - struct nilfs_inode_info *ii; - - if (args->ino != inode->i_ino || args->root != NILFS_I(inode)->i_root) - return 0; - - ii = NILFS_I(inode); - if (!test_bit(NILFS_I_GCINODE, &ii->i_state)) - return !args->for_gc; - - return args->for_gc && args->cno == ii->i_cno; -} - -static int nilfs_iget_set(struct inode *inode, void *opaque) -{ - struct nilfs_iget_args *args = opaque; - - inode->i_ino = args->ino; - if (args->for_gc) { - NILFS_I(inode)->i_state = 1 << NILFS_I_GCINODE; - NILFS_I(inode)->i_cno = args->cno; - NILFS_I(inode)->i_root = NULL; - } else { - if (args->root && args->ino == NILFS_ROOT_INO) - nilfs_get_root(args->root); - NILFS_I(inode)->i_root = args->root; - } - return 0; -} - -struct inode *nilfs_ilookup(struct super_block *sb, struct nilfs_root *root, - unsigned long ino) -{ - struct nilfs_iget_args args = { - .ino = ino, .root = root, .cno = 0, .for_gc = 0 - }; - - return ilookup5(sb, ino, nilfs_iget_test, &args); -} - -struct inode *nilfs_iget_locked(struct super_block *sb, struct nilfs_root *root, - unsigned long ino) -{ - struct nilfs_iget_args args = { - .ino = ino, .root = root, .cno = 0, .for_gc = 0 - }; - - return iget5_locked(sb, ino, nilfs_iget_test, nilfs_iget_set, &args); -} - -struct inode *nilfs_iget(struct super_block *sb, struct nilfs_root *root, - unsigned long ino) -{ - struct inode *inode; - int err; - - inode = nilfs_iget_locked(sb, root, ino); - if (unlikely(!inode)) - return ERR_PTR(-ENOMEM); - if (!(inode->i_state & I_NEW)) - return inode; - - err = __nilfs_read_inode(sb, root, ino, inode); - if (unlikely(err)) { - iget_failed(inode); - return ERR_PTR(err); - } - unlock_new_inode(inode); - return inode; -} - -struct inode *nilfs_iget_for_gc(struct super_block *sb, unsigned long ino, - __u64 cno) -{ - struct nilfs_iget_args args = { - .ino = ino, .root = NULL, .cno = cno, .for_gc = 1 - }; - struct inode *inode; - int err; - - inode = iget5_locked(sb, ino, nilfs_iget_test, nilfs_iget_set, &args); - if (unlikely(!inode)) - return ERR_PTR(-ENOMEM); - if (!(inode->i_state & I_NEW)) - return inode; - - err = nilfs_init_gcinode(inode); - if (unlikely(err)) { - iget_failed(inode); - return ERR_PTR(err); - } - unlock_new_inode(inode); - return inode; -} - -void nilfs_write_inode_common(struct inode *inode, - struct nilfs_inode *raw_inode, int has_bmap) -{ - struct nilfs_inode_info *ii = NILFS_I(inode); - - raw_inode->i_mode = cpu_to_le16(inode->i_mode); - raw_inode->i_uid = cpu_to_le32(inode->i_uid); - raw_inode->i_gid = cpu_to_le32(inode->i_gid); - raw_inode->i_links_count = cpu_to_le16(inode->i_nlink); - raw_inode->i_size = cpu_to_le64(inode->i_size); - raw_inode->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec); - raw_inode->i_mtime = cpu_to_le64(inode->i_mtime.tv_sec); - raw_inode->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec); - raw_inode->i_mtime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec); - raw_inode->i_blocks = cpu_to_le64(inode->i_blocks); - - raw_inode->i_flags = cpu_to_le32(ii->i_flags); - raw_inode->i_generation = cpu_to_le32(inode->i_generation); - - if (NILFS_ROOT_METADATA_FILE(inode->i_ino)) { - struct the_nilfs *nilfs = inode->i_sb->s_fs_info; - - /* zero-fill unused portion in the case of super root block */ - raw_inode->i_xattr = 0; - raw_inode->i_pad = 0; - memset((void *)raw_inode + sizeof(*raw_inode), 0, - nilfs->ns_inode_size - sizeof(*raw_inode)); - } - - if (has_bmap) - nilfs_bmap_write(ii->i_bmap, raw_inode); - else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) - raw_inode->i_device_code = - cpu_to_le64(huge_encode_dev(inode->i_rdev)); - /* When extending inode, nilfs->ns_inode_size should be checked - for substitutions of appended fields */ -} - -void nilfs_update_inode(struct inode *inode, struct buffer_head *ibh) -{ - ino_t ino = inode->i_ino; - struct nilfs_inode_info *ii = NILFS_I(inode); - struct inode *ifile = ii->i_root->ifile; - struct nilfs_inode *raw_inode; - - raw_inode = nilfs_ifile_map_inode(ifile, ino, ibh); - - if (test_and_clear_bit(NILFS_I_NEW, &ii->i_state)) - memset(raw_inode, 0, NILFS_MDT(ifile)->mi_entry_size); - set_bit(NILFS_I_INODE_DIRTY, &ii->i_state); - - nilfs_write_inode_common(inode, raw_inode, 0); - /* XXX: call with has_bmap = 0 is a workaround to avoid - deadlock of bmap. This delays update of i_bmap to just - before writing */ - nilfs_ifile_unmap_inode(ifile, ino, ibh); -} - -#define NILFS_MAX_TRUNCATE_BLOCKS 16384 /* 64MB for 4KB block */ - -static void nilfs_truncate_bmap(struct nilfs_inode_info *ii, - unsigned long from) -{ - unsigned long b; - int ret; - - if (!test_bit(NILFS_I_BMAP, &ii->i_state)) - return; -repeat: - ret = nilfs_bmap_last_key(ii->i_bmap, &b); - if (ret == -ENOENT) - return; - else if (ret < 0) - goto failed; - - if (b < from) - return; - - b -= min_t(unsigned long, NILFS_MAX_TRUNCATE_BLOCKS, b - from); - ret = nilfs_bmap_truncate(ii->i_bmap, b); - nilfs_relax_pressure_in_lock(ii->vfs_inode.i_sb); - if (!ret || (ret == -ENOMEM && - nilfs_bmap_truncate(ii->i_bmap, b) == 0)) - goto repeat; - -failed: - nilfs_warning(ii->vfs_inode.i_sb, __func__, - "failed to truncate bmap (ino=%lu, err=%d)", - ii->vfs_inode.i_ino, ret); -} - -void nilfs_truncate(struct inode *inode) -{ - unsigned long blkoff; - unsigned int blocksize; - struct nilfs_transaction_info ti; - struct super_block *sb = inode->i_sb; - struct nilfs_inode_info *ii = NILFS_I(inode); - - if (!test_bit(NILFS_I_BMAP, &ii->i_state)) - return; - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) - return; - - blocksize = sb->s_blocksize; - blkoff = (inode->i_size + blocksize - 1) >> sb->s_blocksize_bits; - nilfs_transaction_begin(sb, &ti, 0); /* never fails */ - - block_truncate_page(inode->i_mapping, inode->i_size, nilfs_get_block); - - nilfs_truncate_bmap(ii, blkoff); - - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - if (IS_SYNC(inode)) - nilfs_set_transaction_flag(NILFS_TI_SYNC); - - nilfs_mark_inode_dirty(inode); - nilfs_set_file_dirty(inode, 0); - nilfs_transaction_commit(sb); - /* May construct a logical segment and may fail in sync mode. - But truncate has no return value. */ -} - -static void nilfs_clear_inode(struct inode *inode) -{ - struct nilfs_inode_info *ii = NILFS_I(inode); - struct nilfs_mdt_info *mdi = NILFS_MDT(inode); - - /* - * Free resources allocated in nilfs_read_inode(), here. - */ - BUG_ON(!list_empty(&ii->i_dirty)); - brelse(ii->i_bh); - ii->i_bh = NULL; - - if (mdi && mdi->mi_palloc_cache) - nilfs_palloc_destroy_cache(inode); - - if (test_bit(NILFS_I_BMAP, &ii->i_state)) - nilfs_bmap_clear(ii->i_bmap); - - nilfs_btnode_cache_clear(&ii->i_btnode_cache); - - if (ii->i_root && inode->i_ino == NILFS_ROOT_INO) - nilfs_put_root(ii->i_root); -} - -void nilfs_evict_inode(struct inode *inode) -{ - struct nilfs_transaction_info ti; - struct super_block *sb = inode->i_sb; - struct nilfs_inode_info *ii = NILFS_I(inode); - int ret; - - if (inode->i_nlink || !ii->i_root || unlikely(is_bad_inode(inode))) { - if (inode->i_data.nrpages) - truncate_inode_pages(&inode->i_data, 0); - end_writeback(inode); - nilfs_clear_inode(inode); - return; - } - nilfs_transaction_begin(sb, &ti, 0); /* never fails */ - - if (inode->i_data.nrpages) - truncate_inode_pages(&inode->i_data, 0); - - /* TODO: some of the following operations may fail. */ - nilfs_truncate_bmap(ii, 0); - nilfs_mark_inode_dirty(inode); - end_writeback(inode); - - ret = nilfs_ifile_delete_inode(ii->i_root->ifile, inode->i_ino); - if (!ret) - atomic_dec(&ii->i_root->inodes_count); - - nilfs_clear_inode(inode); - - if (IS_SYNC(inode)) - nilfs_set_transaction_flag(NILFS_TI_SYNC); - nilfs_transaction_commit(sb); - /* May construct a logical segment and may fail in sync mode. - But delete_inode has no return value. */ -} - -int nilfs_setattr(struct dentry *dentry, struct iattr *iattr) -{ - struct nilfs_transaction_info ti; - struct inode *inode = dentry->d_inode; - struct super_block *sb = inode->i_sb; - int err; - - err = inode_change_ok(inode, iattr); - if (err) - return err; - - err = nilfs_transaction_begin(sb, &ti, 0); - if (unlikely(err)) - return err; - - if ((iattr->ia_valid & ATTR_SIZE) && - iattr->ia_size != i_size_read(inode)) { - inode_dio_wait(inode); - - err = vmtruncate(inode, iattr->ia_size); - if (unlikely(err)) - goto out_err; - } - - setattr_copy(inode, iattr); - mark_inode_dirty(inode); - - if (iattr->ia_valid & ATTR_MODE) { - err = nilfs_acl_chmod(inode); - if (unlikely(err)) - goto out_err; - } - - return nilfs_transaction_commit(sb); - -out_err: - nilfs_transaction_abort(sb); - return err; -} - -int nilfs_permission(struct inode *inode, int mask) -{ - struct nilfs_root *root = NILFS_I(inode)->i_root; - if ((mask & MAY_WRITE) && root && - root->cno != NILFS_CPTREE_CURRENT_CNO) - return -EROFS; /* snapshot is not writable */ - - return generic_permission(inode, mask); -} - -int nilfs_load_inode_block(struct inode *inode, struct buffer_head **pbh) -{ - struct the_nilfs *nilfs = inode->i_sb->s_fs_info; - struct nilfs_inode_info *ii = NILFS_I(inode); - int err; - - spin_lock(&nilfs->ns_inode_lock); - if (ii->i_bh == NULL) { - spin_unlock(&nilfs->ns_inode_lock); - err = nilfs_ifile_get_inode_block(ii->i_root->ifile, - inode->i_ino, pbh); - if (unlikely(err)) - return err; - spin_lock(&nilfs->ns_inode_lock); - if (ii->i_bh == NULL) - ii->i_bh = *pbh; - else { - brelse(*pbh); - *pbh = ii->i_bh; - } - } else - *pbh = ii->i_bh; - - get_bh(*pbh); - spin_unlock(&nilfs->ns_inode_lock); - return 0; -} - -int nilfs_inode_dirty(struct inode *inode) -{ - struct nilfs_inode_info *ii = NILFS_I(inode); - struct the_nilfs *nilfs = inode->i_sb->s_fs_info; - int ret = 0; - - if (!list_empty(&ii->i_dirty)) { - spin_lock(&nilfs->ns_inode_lock); - ret = test_bit(NILFS_I_DIRTY, &ii->i_state) || - test_bit(NILFS_I_BUSY, &ii->i_state); - spin_unlock(&nilfs->ns_inode_lock); - } - return ret; -} - -int nilfs_set_file_dirty(struct inode *inode, unsigned nr_dirty) -{ - struct nilfs_inode_info *ii = NILFS_I(inode); - struct the_nilfs *nilfs = inode->i_sb->s_fs_info; - - atomic_add(nr_dirty, &nilfs->ns_ndirtyblks); - - if (test_and_set_bit(NILFS_I_DIRTY, &ii->i_state)) - return 0; - - spin_lock(&nilfs->ns_inode_lock); - if (!test_bit(NILFS_I_QUEUED, &ii->i_state) && - !test_bit(NILFS_I_BUSY, &ii->i_state)) { - /* Because this routine may race with nilfs_dispose_list(), - we have to check NILFS_I_QUEUED here, too. */ - if (list_empty(&ii->i_dirty) && igrab(inode) == NULL) { - /* This will happen when somebody is freeing - this inode. */ - nilfs_warning(inode->i_sb, __func__, - "cannot get inode (ino=%lu)\n", - inode->i_ino); - spin_unlock(&nilfs->ns_inode_lock); - return -EINVAL; /* NILFS_I_DIRTY may remain for - freeing inode */ - } - list_move_tail(&ii->i_dirty, &nilfs->ns_dirty_files); - set_bit(NILFS_I_QUEUED, &ii->i_state); - } - spin_unlock(&nilfs->ns_inode_lock); - return 0; -} - -int nilfs_mark_inode_dirty(struct inode *inode) -{ - struct buffer_head *ibh; - int err; - - err = nilfs_load_inode_block(inode, &ibh); - if (unlikely(err)) { - nilfs_warning(inode->i_sb, __func__, - "failed to reget inode block.\n"); - return err; - } - nilfs_update_inode(inode, ibh); - mark_buffer_dirty(ibh); - nilfs_mdt_mark_dirty(NILFS_I(inode)->i_root->ifile); - brelse(ibh); - return 0; -} - -/** - * nilfs_dirty_inode - reflect changes on given inode to an inode block. - * @inode: inode of the file to be registered. - * - * nilfs_dirty_inode() loads a inode block containing the specified - * @inode and copies data from a nilfs_inode to a corresponding inode - * entry in the inode block. This operation is excluded from the segment - * construction. This function can be called both as a single operation - * and as a part of indivisible file operations. - */ -void nilfs_dirty_inode(struct inode *inode, int flags) -{ - struct nilfs_transaction_info ti; - struct nilfs_mdt_info *mdi = NILFS_MDT(inode); - - if (is_bad_inode(inode)) { - nilfs_warning(inode->i_sb, __func__, - "tried to mark bad_inode dirty. ignored.\n"); - dump_stack(); - return; - } - if (mdi) { - nilfs_mdt_mark_dirty(inode); - return; - } - nilfs_transaction_begin(inode->i_sb, &ti, 0); - nilfs_mark_inode_dirty(inode); - nilfs_transaction_commit(inode->i_sb); /* never fails */ -} - -int nilfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, - __u64 start, __u64 len) -{ - struct the_nilfs *nilfs = inode->i_sb->s_fs_info; - __u64 logical = 0, phys = 0, size = 0; - __u32 flags = 0; - loff_t isize; - sector_t blkoff, end_blkoff; - sector_t delalloc_blkoff; - unsigned long delalloc_blklen; - unsigned int blkbits = inode->i_blkbits; - int ret, n; - - ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC); - if (ret) - return ret; - - mutex_lock(&inode->i_mutex); - - isize = i_size_read(inode); - - blkoff = start >> blkbits; - end_blkoff = (start + len - 1) >> blkbits; - - delalloc_blklen = nilfs_find_uncommitted_extent(inode, blkoff, - &delalloc_blkoff); - - do { - __u64 blkphy; - unsigned int maxblocks; - - if (delalloc_blklen && blkoff == delalloc_blkoff) { - if (size) { - /* End of the current extent */ - ret = fiemap_fill_next_extent( - fieinfo, logical, phys, size, flags); - if (ret) - break; - } - if (blkoff > end_blkoff) - break; - - flags = FIEMAP_EXTENT_MERGED | FIEMAP_EXTENT_DELALLOC; - logical = blkoff << blkbits; - phys = 0; - size = delalloc_blklen << blkbits; - - blkoff = delalloc_blkoff + delalloc_blklen; - delalloc_blklen = nilfs_find_uncommitted_extent( - inode, blkoff, &delalloc_blkoff); - continue; - } - - /* - * Limit the number of blocks that we look up so as - * not to get into the next delayed allocation extent. - */ - maxblocks = INT_MAX; - if (delalloc_blklen) - maxblocks = min_t(sector_t, delalloc_blkoff - blkoff, - maxblocks); - blkphy = 0; - - down_read(&NILFS_MDT(nilfs->ns_dat)->mi_sem); - n = nilfs_bmap_lookup_contig( - NILFS_I(inode)->i_bmap, blkoff, &blkphy, maxblocks); - up_read(&NILFS_MDT(nilfs->ns_dat)->mi_sem); - - if (n < 0) { - int past_eof; - - if (unlikely(n != -ENOENT)) - break; /* error */ - - /* HOLE */ - blkoff++; - past_eof = ((blkoff << blkbits) >= isize); - - if (size) { - /* End of the current extent */ - - if (past_eof) - flags |= FIEMAP_EXTENT_LAST; - - ret = fiemap_fill_next_extent( - fieinfo, logical, phys, size, flags); - if (ret) - break; - size = 0; - } - if (blkoff > end_blkoff || past_eof) - break; - } else { - if (size) { - if (phys && blkphy << blkbits == phys + size) { - /* The current extent goes on */ - size += n << blkbits; - } else { - /* Terminate the current extent */ - ret = fiemap_fill_next_extent( - fieinfo, logical, phys, size, - flags); - if (ret || blkoff > end_blkoff) - break; - - /* Start another extent */ - flags = FIEMAP_EXTENT_MERGED; - logical = blkoff << blkbits; - phys = blkphy << blkbits; - size = n << blkbits; - } - } else { - /* Start a new extent */ - flags = FIEMAP_EXTENT_MERGED; - logical = blkoff << blkbits; - phys = blkphy << blkbits; - size = n << blkbits; - } - blkoff += n; - } - cond_resched(); - } while (true); - - /* If ret is 1 then we just hit the end of the extent array */ - if (ret == 1) - ret = 0; - - mutex_unlock(&inode->i_mutex); - return ret; -} diff --git a/ANDROID_3.4.5/fs/nilfs2/ioctl.c b/ANDROID_3.4.5/fs/nilfs2/ioctl.c deleted file mode 100644 index 2a70fce7..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/ioctl.c +++ /dev/null @@ -1,868 +0,0 @@ -/* - * ioctl.c - NILFS ioctl operations. - * - * Copyright (C) 2007, 2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Koji Sato <koji@osrg.net>. - */ - -#include <linux/fs.h> -#include <linux/wait.h> -#include <linux/slab.h> -#include <linux/capability.h> /* capable() */ -#include <linux/uaccess.h> /* copy_from_user(), copy_to_user() */ -#include <linux/vmalloc.h> -#include <linux/compat.h> /* compat_ptr() */ -#include <linux/mount.h> /* mnt_want_write_file(), mnt_drop_write_file() */ -#include <linux/buffer_head.h> -#include <linux/nilfs2_fs.h> -#include "nilfs.h" -#include "segment.h" -#include "bmap.h" -#include "cpfile.h" -#include "sufile.h" -#include "dat.h" - - -static int nilfs_ioctl_wrap_copy(struct the_nilfs *nilfs, - struct nilfs_argv *argv, int dir, - ssize_t (*dofunc)(struct the_nilfs *, - __u64 *, int, - void *, size_t, size_t)) -{ - void *buf; - void __user *base = (void __user *)(unsigned long)argv->v_base; - size_t maxmembs, total, n; - ssize_t nr; - int ret, i; - __u64 pos, ppos; - - if (argv->v_nmembs == 0) - return 0; - - if (argv->v_size > PAGE_SIZE) - return -EINVAL; - - buf = (void *)__get_free_pages(GFP_NOFS, 0); - if (unlikely(!buf)) - return -ENOMEM; - maxmembs = PAGE_SIZE / argv->v_size; - - ret = 0; - total = 0; - pos = argv->v_index; - for (i = 0; i < argv->v_nmembs; i += n) { - n = (argv->v_nmembs - i < maxmembs) ? - argv->v_nmembs - i : maxmembs; - if ((dir & _IOC_WRITE) && - copy_from_user(buf, base + argv->v_size * i, - argv->v_size * n)) { - ret = -EFAULT; - break; - } - ppos = pos; - nr = dofunc(nilfs, &pos, argv->v_flags, buf, argv->v_size, - n); - if (nr < 0) { - ret = nr; - break; - } - if ((dir & _IOC_READ) && - copy_to_user(base + argv->v_size * i, buf, - argv->v_size * nr)) { - ret = -EFAULT; - break; - } - total += nr; - if ((size_t)nr < n) - break; - if (pos == ppos) - pos += n; - } - argv->v_nmembs = total; - - free_pages((unsigned long)buf, 0); - return ret; -} - -static int nilfs_ioctl_getflags(struct inode *inode, void __user *argp) -{ - unsigned int flags = NILFS_I(inode)->i_flags & FS_FL_USER_VISIBLE; - - return put_user(flags, (int __user *)argp); -} - -static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp, - void __user *argp) -{ - struct nilfs_transaction_info ti; - unsigned int flags, oldflags; - int ret; - - if (!inode_owner_or_capable(inode)) - return -EACCES; - - if (get_user(flags, (int __user *)argp)) - return -EFAULT; - - ret = mnt_want_write_file(filp); - if (ret) - return ret; - - flags = nilfs_mask_flags(inode->i_mode, flags); - - mutex_lock(&inode->i_mutex); - - oldflags = NILFS_I(inode)->i_flags; - - /* - * The IMMUTABLE and APPEND_ONLY flags can only be changed by the - * relevant capability. - */ - ret = -EPERM; - if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) && - !capable(CAP_LINUX_IMMUTABLE)) - goto out; - - ret = nilfs_transaction_begin(inode->i_sb, &ti, 0); - if (ret) - goto out; - - NILFS_I(inode)->i_flags = (oldflags & ~FS_FL_USER_MODIFIABLE) | - (flags & FS_FL_USER_MODIFIABLE); - - nilfs_set_inode_flags(inode); - inode->i_ctime = CURRENT_TIME; - if (IS_SYNC(inode)) - nilfs_set_transaction_flag(NILFS_TI_SYNC); - - nilfs_mark_inode_dirty(inode); - ret = nilfs_transaction_commit(inode->i_sb); -out: - mutex_unlock(&inode->i_mutex); - mnt_drop_write_file(filp); - return ret; -} - -static int nilfs_ioctl_getversion(struct inode *inode, void __user *argp) -{ - return put_user(inode->i_generation, (int __user *)argp); -} - -static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp, - unsigned int cmd, void __user *argp) -{ - struct the_nilfs *nilfs = inode->i_sb->s_fs_info; - struct nilfs_transaction_info ti; - struct nilfs_cpmode cpmode; - int ret; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - ret = mnt_want_write_file(filp); - if (ret) - return ret; - - ret = -EFAULT; - if (copy_from_user(&cpmode, argp, sizeof(cpmode))) - goto out; - - down_read(&inode->i_sb->s_umount); - - nilfs_transaction_begin(inode->i_sb, &ti, 0); - ret = nilfs_cpfile_change_cpmode( - nilfs->ns_cpfile, cpmode.cm_cno, cpmode.cm_mode); - if (unlikely(ret < 0)) - nilfs_transaction_abort(inode->i_sb); - else - nilfs_transaction_commit(inode->i_sb); /* never fails */ - - up_read(&inode->i_sb->s_umount); -out: - mnt_drop_write_file(filp); - return ret; -} - -static int -nilfs_ioctl_delete_checkpoint(struct inode *inode, struct file *filp, - unsigned int cmd, void __user *argp) -{ - struct the_nilfs *nilfs = inode->i_sb->s_fs_info; - struct nilfs_transaction_info ti; - __u64 cno; - int ret; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - ret = mnt_want_write_file(filp); - if (ret) - return ret; - - ret = -EFAULT; - if (copy_from_user(&cno, argp, sizeof(cno))) - goto out; - - nilfs_transaction_begin(inode->i_sb, &ti, 0); - ret = nilfs_cpfile_delete_checkpoint(nilfs->ns_cpfile, cno); - if (unlikely(ret < 0)) - nilfs_transaction_abort(inode->i_sb); - else - nilfs_transaction_commit(inode->i_sb); /* never fails */ -out: - mnt_drop_write_file(filp); - return ret; -} - -static ssize_t -nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, - void *buf, size_t size, size_t nmembs) -{ - int ret; - - down_read(&nilfs->ns_segctor_sem); - ret = nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf, - size, nmembs); - up_read(&nilfs->ns_segctor_sem); - return ret; -} - -static int nilfs_ioctl_get_cpstat(struct inode *inode, struct file *filp, - unsigned int cmd, void __user *argp) -{ - struct the_nilfs *nilfs = inode->i_sb->s_fs_info; - struct nilfs_cpstat cpstat; - int ret; - - down_read(&nilfs->ns_segctor_sem); - ret = nilfs_cpfile_get_stat(nilfs->ns_cpfile, &cpstat); - up_read(&nilfs->ns_segctor_sem); - if (ret < 0) - return ret; - - if (copy_to_user(argp, &cpstat, sizeof(cpstat))) - ret = -EFAULT; - return ret; -} - -static ssize_t -nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, - void *buf, size_t size, size_t nmembs) -{ - int ret; - - down_read(&nilfs->ns_segctor_sem); - ret = nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, size, - nmembs); - up_read(&nilfs->ns_segctor_sem); - return ret; -} - -static int nilfs_ioctl_get_sustat(struct inode *inode, struct file *filp, - unsigned int cmd, void __user *argp) -{ - struct the_nilfs *nilfs = inode->i_sb->s_fs_info; - struct nilfs_sustat sustat; - int ret; - - down_read(&nilfs->ns_segctor_sem); - ret = nilfs_sufile_get_stat(nilfs->ns_sufile, &sustat); - up_read(&nilfs->ns_segctor_sem); - if (ret < 0) - return ret; - - if (copy_to_user(argp, &sustat, sizeof(sustat))) - ret = -EFAULT; - return ret; -} - -static ssize_t -nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, - void *buf, size_t size, size_t nmembs) -{ - int ret; - - down_read(&nilfs->ns_segctor_sem); - ret = nilfs_dat_get_vinfo(nilfs->ns_dat, buf, size, nmembs); - up_read(&nilfs->ns_segctor_sem); - return ret; -} - -static ssize_t -nilfs_ioctl_do_get_bdescs(struct the_nilfs *nilfs, __u64 *posp, int flags, - void *buf, size_t size, size_t nmembs) -{ - struct nilfs_bmap *bmap = NILFS_I(nilfs->ns_dat)->i_bmap; - struct nilfs_bdesc *bdescs = buf; - int ret, i; - - down_read(&nilfs->ns_segctor_sem); - for (i = 0; i < nmembs; i++) { - ret = nilfs_bmap_lookup_at_level(bmap, - bdescs[i].bd_offset, - bdescs[i].bd_level + 1, - &bdescs[i].bd_blocknr); - if (ret < 0) { - if (ret != -ENOENT) { - up_read(&nilfs->ns_segctor_sem); - return ret; - } - bdescs[i].bd_blocknr = 0; - } - } - up_read(&nilfs->ns_segctor_sem); - return nmembs; -} - -static int nilfs_ioctl_get_bdescs(struct inode *inode, struct file *filp, - unsigned int cmd, void __user *argp) -{ - struct the_nilfs *nilfs = inode->i_sb->s_fs_info; - struct nilfs_argv argv; - int ret; - - if (copy_from_user(&argv, argp, sizeof(argv))) - return -EFAULT; - - if (argv.v_size != sizeof(struct nilfs_bdesc)) - return -EINVAL; - - ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), - nilfs_ioctl_do_get_bdescs); - if (ret < 0) - return ret; - - if (copy_to_user(argp, &argv, sizeof(argv))) - ret = -EFAULT; - return ret; -} - -static int nilfs_ioctl_move_inode_block(struct inode *inode, - struct nilfs_vdesc *vdesc, - struct list_head *buffers) -{ - struct buffer_head *bh; - int ret; - - if (vdesc->vd_flags == 0) - ret = nilfs_gccache_submit_read_data( - inode, vdesc->vd_offset, vdesc->vd_blocknr, - vdesc->vd_vblocknr, &bh); - else - ret = nilfs_gccache_submit_read_node( - inode, vdesc->vd_blocknr, vdesc->vd_vblocknr, &bh); - - if (unlikely(ret < 0)) { - if (ret == -ENOENT) - printk(KERN_CRIT - "%s: invalid virtual block address (%s): " - "ino=%llu, cno=%llu, offset=%llu, " - "blocknr=%llu, vblocknr=%llu\n", - __func__, vdesc->vd_flags ? "node" : "data", - (unsigned long long)vdesc->vd_ino, - (unsigned long long)vdesc->vd_cno, - (unsigned long long)vdesc->vd_offset, - (unsigned long long)vdesc->vd_blocknr, - (unsigned long long)vdesc->vd_vblocknr); - return ret; - } - if (unlikely(!list_empty(&bh->b_assoc_buffers))) { - printk(KERN_CRIT "%s: conflicting %s buffer: ino=%llu, " - "cno=%llu, offset=%llu, blocknr=%llu, vblocknr=%llu\n", - __func__, vdesc->vd_flags ? "node" : "data", - (unsigned long long)vdesc->vd_ino, - (unsigned long long)vdesc->vd_cno, - (unsigned long long)vdesc->vd_offset, - (unsigned long long)vdesc->vd_blocknr, - (unsigned long long)vdesc->vd_vblocknr); - brelse(bh); - return -EEXIST; - } - list_add_tail(&bh->b_assoc_buffers, buffers); - return 0; -} - -static int nilfs_ioctl_move_blocks(struct super_block *sb, - struct nilfs_argv *argv, void *buf) -{ - size_t nmembs = argv->v_nmembs; - struct the_nilfs *nilfs = sb->s_fs_info; - struct inode *inode; - struct nilfs_vdesc *vdesc; - struct buffer_head *bh, *n; - LIST_HEAD(buffers); - ino_t ino; - __u64 cno; - int i, ret; - - for (i = 0, vdesc = buf; i < nmembs; ) { - ino = vdesc->vd_ino; - cno = vdesc->vd_cno; - inode = nilfs_iget_for_gc(sb, ino, cno); - if (IS_ERR(inode)) { - ret = PTR_ERR(inode); - goto failed; - } - if (list_empty(&NILFS_I(inode)->i_dirty)) { - /* - * Add the inode to GC inode list. Garbage Collection - * is serialized and no two processes manipulate the - * list simultaneously. - */ - igrab(inode); - list_add(&NILFS_I(inode)->i_dirty, - &nilfs->ns_gc_inodes); - } - - do { - ret = nilfs_ioctl_move_inode_block(inode, vdesc, - &buffers); - if (unlikely(ret < 0)) { - iput(inode); - goto failed; - } - vdesc++; - } while (++i < nmembs && - vdesc->vd_ino == ino && vdesc->vd_cno == cno); - - iput(inode); /* The inode still remains in GC inode list */ - } - - list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) { - ret = nilfs_gccache_wait_and_mark_dirty(bh); - if (unlikely(ret < 0)) { - WARN_ON(ret == -EEXIST); - goto failed; - } - list_del_init(&bh->b_assoc_buffers); - brelse(bh); - } - return nmembs; - - failed: - list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) { - list_del_init(&bh->b_assoc_buffers); - brelse(bh); - } - return ret; -} - -static int nilfs_ioctl_delete_checkpoints(struct the_nilfs *nilfs, - struct nilfs_argv *argv, void *buf) -{ - size_t nmembs = argv->v_nmembs; - struct inode *cpfile = nilfs->ns_cpfile; - struct nilfs_period *periods = buf; - int ret, i; - - for (i = 0; i < nmembs; i++) { - ret = nilfs_cpfile_delete_checkpoints( - cpfile, periods[i].p_start, periods[i].p_end); - if (ret < 0) - return ret; - } - return nmembs; -} - -static int nilfs_ioctl_free_vblocknrs(struct the_nilfs *nilfs, - struct nilfs_argv *argv, void *buf) -{ - size_t nmembs = argv->v_nmembs; - int ret; - - ret = nilfs_dat_freev(nilfs->ns_dat, buf, nmembs); - - return (ret < 0) ? ret : nmembs; -} - -static int nilfs_ioctl_mark_blocks_dirty(struct the_nilfs *nilfs, - struct nilfs_argv *argv, void *buf) -{ - size_t nmembs = argv->v_nmembs; - struct nilfs_bmap *bmap = NILFS_I(nilfs->ns_dat)->i_bmap; - struct nilfs_bdesc *bdescs = buf; - int ret, i; - - for (i = 0; i < nmembs; i++) { - /* XXX: use macro or inline func to check liveness */ - ret = nilfs_bmap_lookup_at_level(bmap, - bdescs[i].bd_offset, - bdescs[i].bd_level + 1, - &bdescs[i].bd_blocknr); - if (ret < 0) { - if (ret != -ENOENT) - return ret; - bdescs[i].bd_blocknr = 0; - } - if (bdescs[i].bd_blocknr != bdescs[i].bd_oblocknr) - /* skip dead block */ - continue; - if (bdescs[i].bd_level == 0) { - ret = nilfs_mdt_mark_block_dirty(nilfs->ns_dat, - bdescs[i].bd_offset); - if (ret < 0) { - WARN_ON(ret == -ENOENT); - return ret; - } - } else { - ret = nilfs_bmap_mark(bmap, bdescs[i].bd_offset, - bdescs[i].bd_level); - if (ret < 0) { - WARN_ON(ret == -ENOENT); - return ret; - } - } - } - return nmembs; -} - -int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs, - struct nilfs_argv *argv, void **kbufs) -{ - const char *msg; - int ret; - - ret = nilfs_ioctl_delete_checkpoints(nilfs, &argv[1], kbufs[1]); - if (ret < 0) { - /* - * can safely abort because checkpoints can be removed - * independently. - */ - msg = "cannot delete checkpoints"; - goto failed; - } - ret = nilfs_ioctl_free_vblocknrs(nilfs, &argv[2], kbufs[2]); - if (ret < 0) { - /* - * can safely abort because DAT file is updated atomically - * using a copy-on-write technique. - */ - msg = "cannot delete virtual blocks from DAT file"; - goto failed; - } - ret = nilfs_ioctl_mark_blocks_dirty(nilfs, &argv[3], kbufs[3]); - if (ret < 0) { - /* - * can safely abort because the operation is nondestructive. - */ - msg = "cannot mark copying blocks dirty"; - goto failed; - } - return 0; - - failed: - printk(KERN_ERR "NILFS: GC failed during preparation: %s: err=%d\n", - msg, ret); - return ret; -} - -static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp, - unsigned int cmd, void __user *argp) -{ - struct nilfs_argv argv[5]; - static const size_t argsz[5] = { - sizeof(struct nilfs_vdesc), - sizeof(struct nilfs_period), - sizeof(__u64), - sizeof(struct nilfs_bdesc), - sizeof(__u64), - }; - void __user *base; - void *kbufs[5]; - struct the_nilfs *nilfs; - size_t len, nsegs; - int n, ret; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - ret = mnt_want_write_file(filp); - if (ret) - return ret; - - ret = -EFAULT; - if (copy_from_user(argv, argp, sizeof(argv))) - goto out; - - ret = -EINVAL; - nsegs = argv[4].v_nmembs; - if (argv[4].v_size != argsz[4]) - goto out; - if (nsegs > UINT_MAX / sizeof(__u64)) - goto out; - - /* - * argv[4] points to segment numbers this ioctl cleans. We - * use kmalloc() for its buffer because memory used for the - * segment numbers is enough small. - */ - kbufs[4] = memdup_user((void __user *)(unsigned long)argv[4].v_base, - nsegs * sizeof(__u64)); - if (IS_ERR(kbufs[4])) { - ret = PTR_ERR(kbufs[4]); - goto out; - } - nilfs = inode->i_sb->s_fs_info; - - for (n = 0; n < 4; n++) { - ret = -EINVAL; - if (argv[n].v_size != argsz[n]) - goto out_free; - - if (argv[n].v_nmembs > nsegs * nilfs->ns_blocks_per_segment) - goto out_free; - - if (argv[n].v_nmembs >= UINT_MAX / argv[n].v_size) - goto out_free; - - len = argv[n].v_size * argv[n].v_nmembs; - base = (void __user *)(unsigned long)argv[n].v_base; - if (len == 0) { - kbufs[n] = NULL; - continue; - } - - kbufs[n] = vmalloc(len); - if (!kbufs[n]) { - ret = -ENOMEM; - goto out_free; - } - if (copy_from_user(kbufs[n], base, len)) { - ret = -EFAULT; - vfree(kbufs[n]); - goto out_free; - } - } - - /* - * nilfs_ioctl_move_blocks() will call nilfs_iget_for_gc(), - * which will operates an inode list without blocking. - * To protect the list from concurrent operations, - * nilfs_ioctl_move_blocks should be atomic operation. - */ - if (test_and_set_bit(THE_NILFS_GC_RUNNING, &nilfs->ns_flags)) { - ret = -EBUSY; - goto out_free; - } - - vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE); - - ret = nilfs_ioctl_move_blocks(inode->i_sb, &argv[0], kbufs[0]); - if (ret < 0) - printk(KERN_ERR "NILFS: GC failed during preparation: " - "cannot read source blocks: err=%d\n", ret); - else - ret = nilfs_clean_segments(inode->i_sb, argv, kbufs); - - nilfs_remove_all_gcinodes(nilfs); - clear_nilfs_gc_running(nilfs); - -out_free: - while (--n >= 0) - vfree(kbufs[n]); - kfree(kbufs[4]); -out: - mnt_drop_write_file(filp); - return ret; -} - -static int nilfs_ioctl_sync(struct inode *inode, struct file *filp, - unsigned int cmd, void __user *argp) -{ - __u64 cno; - int ret; - struct the_nilfs *nilfs; - - ret = nilfs_construct_segment(inode->i_sb); - if (ret < 0) - return ret; - - if (argp != NULL) { - nilfs = inode->i_sb->s_fs_info; - down_read(&nilfs->ns_segctor_sem); - cno = nilfs->ns_cno - 1; - up_read(&nilfs->ns_segctor_sem); - if (copy_to_user(argp, &cno, sizeof(cno))) - return -EFAULT; - } - return 0; -} - -static int nilfs_ioctl_resize(struct inode *inode, struct file *filp, - void __user *argp) -{ - __u64 newsize; - int ret = -EPERM; - - if (!capable(CAP_SYS_ADMIN)) - goto out; - - ret = mnt_want_write_file(filp); - if (ret) - goto out; - - ret = -EFAULT; - if (copy_from_user(&newsize, argp, sizeof(newsize))) - goto out_drop_write; - - ret = nilfs_resize_fs(inode->i_sb, newsize); - -out_drop_write: - mnt_drop_write_file(filp); -out: - return ret; -} - -static int nilfs_ioctl_set_alloc_range(struct inode *inode, void __user *argp) -{ - struct the_nilfs *nilfs = inode->i_sb->s_fs_info; - __u64 range[2]; - __u64 minseg, maxseg; - unsigned long segbytes; - int ret = -EPERM; - - if (!capable(CAP_SYS_ADMIN)) - goto out; - - ret = -EFAULT; - if (copy_from_user(range, argp, sizeof(__u64[2]))) - goto out; - - ret = -ERANGE; - if (range[1] > i_size_read(inode->i_sb->s_bdev->bd_inode)) - goto out; - - segbytes = nilfs->ns_blocks_per_segment * nilfs->ns_blocksize; - - minseg = range[0] + segbytes - 1; - do_div(minseg, segbytes); - maxseg = NILFS_SB2_OFFSET_BYTES(range[1]); - do_div(maxseg, segbytes); - maxseg--; - - ret = nilfs_sufile_set_alloc_range(nilfs->ns_sufile, minseg, maxseg); -out: - return ret; -} - -static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp, - unsigned int cmd, void __user *argp, - size_t membsz, - ssize_t (*dofunc)(struct the_nilfs *, - __u64 *, int, - void *, size_t, size_t)) - -{ - struct the_nilfs *nilfs = inode->i_sb->s_fs_info; - struct nilfs_argv argv; - int ret; - - if (copy_from_user(&argv, argp, sizeof(argv))) - return -EFAULT; - - if (argv.v_size < membsz) - return -EINVAL; - - ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), dofunc); - if (ret < 0) - return ret; - - if (copy_to_user(argp, &argv, sizeof(argv))) - ret = -EFAULT; - return ret; -} - -long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct inode *inode = filp->f_dentry->d_inode; - void __user *argp = (void __user *)arg; - - switch (cmd) { - case FS_IOC_GETFLAGS: - return nilfs_ioctl_getflags(inode, argp); - case FS_IOC_SETFLAGS: - return nilfs_ioctl_setflags(inode, filp, argp); - case FS_IOC_GETVERSION: - return nilfs_ioctl_getversion(inode, argp); - case NILFS_IOCTL_CHANGE_CPMODE: - return nilfs_ioctl_change_cpmode(inode, filp, cmd, argp); - case NILFS_IOCTL_DELETE_CHECKPOINT: - return nilfs_ioctl_delete_checkpoint(inode, filp, cmd, argp); - case NILFS_IOCTL_GET_CPINFO: - return nilfs_ioctl_get_info(inode, filp, cmd, argp, - sizeof(struct nilfs_cpinfo), - nilfs_ioctl_do_get_cpinfo); - case NILFS_IOCTL_GET_CPSTAT: - return nilfs_ioctl_get_cpstat(inode, filp, cmd, argp); - case NILFS_IOCTL_GET_SUINFO: - return nilfs_ioctl_get_info(inode, filp, cmd, argp, - sizeof(struct nilfs_suinfo), - nilfs_ioctl_do_get_suinfo); - case NILFS_IOCTL_GET_SUSTAT: - return nilfs_ioctl_get_sustat(inode, filp, cmd, argp); - case NILFS_IOCTL_GET_VINFO: - return nilfs_ioctl_get_info(inode, filp, cmd, argp, - sizeof(struct nilfs_vinfo), - nilfs_ioctl_do_get_vinfo); - case NILFS_IOCTL_GET_BDESCS: - return nilfs_ioctl_get_bdescs(inode, filp, cmd, argp); - case NILFS_IOCTL_CLEAN_SEGMENTS: - return nilfs_ioctl_clean_segments(inode, filp, cmd, argp); - case NILFS_IOCTL_SYNC: - return nilfs_ioctl_sync(inode, filp, cmd, argp); - case NILFS_IOCTL_RESIZE: - return nilfs_ioctl_resize(inode, filp, argp); - case NILFS_IOCTL_SET_ALLOC_RANGE: - return nilfs_ioctl_set_alloc_range(inode, argp); - default: - return -ENOTTY; - } -} - -#ifdef CONFIG_COMPAT -long nilfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case FS_IOC32_GETFLAGS: - cmd = FS_IOC_GETFLAGS; - break; - case FS_IOC32_SETFLAGS: - cmd = FS_IOC_SETFLAGS; - break; - case FS_IOC32_GETVERSION: - cmd = FS_IOC_GETVERSION; - break; - case NILFS_IOCTL_CHANGE_CPMODE: - case NILFS_IOCTL_DELETE_CHECKPOINT: - case NILFS_IOCTL_GET_CPINFO: - case NILFS_IOCTL_GET_CPSTAT: - case NILFS_IOCTL_GET_SUINFO: - case NILFS_IOCTL_GET_SUSTAT: - case NILFS_IOCTL_GET_VINFO: - case NILFS_IOCTL_GET_BDESCS: - case NILFS_IOCTL_CLEAN_SEGMENTS: - case NILFS_IOCTL_SYNC: - case NILFS_IOCTL_RESIZE: - case NILFS_IOCTL_SET_ALLOC_RANGE: - break; - default: - return -ENOIOCTLCMD; - } - return nilfs_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); -} -#endif diff --git a/ANDROID_3.4.5/fs/nilfs2/mdt.c b/ANDROID_3.4.5/fs/nilfs2/mdt.c deleted file mode 100644 index f9897d09..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/mdt.c +++ /dev/null @@ -1,589 +0,0 @@ -/* - * mdt.c - meta data file for NILFS - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Ryusuke Konishi <ryusuke@osrg.net> - */ - -#include <linux/buffer_head.h> -#include <linux/mpage.h> -#include <linux/mm.h> -#include <linux/writeback.h> -#include <linux/backing-dev.h> -#include <linux/swap.h> -#include <linux/slab.h> -#include "nilfs.h" -#include "btnode.h" -#include "segment.h" -#include "page.h" -#include "mdt.h" - - -#define NILFS_MDT_MAX_RA_BLOCKS (16 - 1) - - -static int -nilfs_mdt_insert_new_block(struct inode *inode, unsigned long block, - struct buffer_head *bh, - void (*init_block)(struct inode *, - struct buffer_head *, void *)) -{ - struct nilfs_inode_info *ii = NILFS_I(inode); - void *kaddr; - int ret; - - /* Caller exclude read accesses using page lock */ - - /* set_buffer_new(bh); */ - bh->b_blocknr = 0; - - ret = nilfs_bmap_insert(ii->i_bmap, block, (unsigned long)bh); - if (unlikely(ret)) - return ret; - - set_buffer_mapped(bh); - - kaddr = kmap_atomic(bh->b_page); - memset(kaddr + bh_offset(bh), 0, 1 << inode->i_blkbits); - if (init_block) - init_block(inode, bh, kaddr); - flush_dcache_page(bh->b_page); - kunmap_atomic(kaddr); - - set_buffer_uptodate(bh); - mark_buffer_dirty(bh); - nilfs_mdt_mark_dirty(inode); - return 0; -} - -static int nilfs_mdt_create_block(struct inode *inode, unsigned long block, - struct buffer_head **out_bh, - void (*init_block)(struct inode *, - struct buffer_head *, - void *)) -{ - struct super_block *sb = inode->i_sb; - struct nilfs_transaction_info ti; - struct buffer_head *bh; - int err; - - nilfs_transaction_begin(sb, &ti, 0); - - err = -ENOMEM; - bh = nilfs_grab_buffer(inode, inode->i_mapping, block, 0); - if (unlikely(!bh)) - goto failed_unlock; - - err = -EEXIST; - if (buffer_uptodate(bh)) - goto failed_bh; - - wait_on_buffer(bh); - if (buffer_uptodate(bh)) - goto failed_bh; - - bh->b_bdev = sb->s_bdev; - err = nilfs_mdt_insert_new_block(inode, block, bh, init_block); - if (likely(!err)) { - get_bh(bh); - *out_bh = bh; - } - - failed_bh: - unlock_page(bh->b_page); - page_cache_release(bh->b_page); - brelse(bh); - - failed_unlock: - if (likely(!err)) - err = nilfs_transaction_commit(sb); - else - nilfs_transaction_abort(sb); - - return err; -} - -static int -nilfs_mdt_submit_block(struct inode *inode, unsigned long blkoff, - int mode, struct buffer_head **out_bh) -{ - struct buffer_head *bh; - __u64 blknum = 0; - int ret = -ENOMEM; - - bh = nilfs_grab_buffer(inode, inode->i_mapping, blkoff, 0); - if (unlikely(!bh)) - goto failed; - - ret = -EEXIST; /* internal code */ - if (buffer_uptodate(bh)) - goto out; - - if (mode == READA) { - if (!trylock_buffer(bh)) { - ret = -EBUSY; - goto failed_bh; - } - } else /* mode == READ */ - lock_buffer(bh); - - if (buffer_uptodate(bh)) { - unlock_buffer(bh); - goto out; - } - - ret = nilfs_bmap_lookup(NILFS_I(inode)->i_bmap, blkoff, &blknum); - if (unlikely(ret)) { - unlock_buffer(bh); - goto failed_bh; - } - map_bh(bh, inode->i_sb, (sector_t)blknum); - - bh->b_end_io = end_buffer_read_sync; - get_bh(bh); - submit_bh(mode, bh); - ret = 0; - out: - get_bh(bh); - *out_bh = bh; - - failed_bh: - unlock_page(bh->b_page); - page_cache_release(bh->b_page); - brelse(bh); - failed: - return ret; -} - -static int nilfs_mdt_read_block(struct inode *inode, unsigned long block, - int readahead, struct buffer_head **out_bh) -{ - struct buffer_head *first_bh, *bh; - unsigned long blkoff; - int i, nr_ra_blocks = NILFS_MDT_MAX_RA_BLOCKS; - int err; - - err = nilfs_mdt_submit_block(inode, block, READ, &first_bh); - if (err == -EEXIST) /* internal code */ - goto out; - - if (unlikely(err)) - goto failed; - - if (readahead) { - blkoff = block + 1; - for (i = 0; i < nr_ra_blocks; i++, blkoff++) { - err = nilfs_mdt_submit_block(inode, blkoff, READA, &bh); - if (likely(!err || err == -EEXIST)) - brelse(bh); - else if (err != -EBUSY) - break; - /* abort readahead if bmap lookup failed */ - if (!buffer_locked(first_bh)) - goto out_no_wait; - } - } - - wait_on_buffer(first_bh); - - out_no_wait: - err = -EIO; - if (!buffer_uptodate(first_bh)) - goto failed_bh; - out: - *out_bh = first_bh; - return 0; - - failed_bh: - brelse(first_bh); - failed: - return err; -} - -/** - * nilfs_mdt_get_block - read or create a buffer on meta data file. - * @inode: inode of the meta data file - * @blkoff: block offset - * @create: create flag - * @init_block: initializer used for newly allocated block - * @out_bh: output of a pointer to the buffer_head - * - * nilfs_mdt_get_block() looks up the specified buffer and tries to create - * a new buffer if @create is not zero. On success, the returned buffer is - * assured to be either existing or formatted using a buffer lock on success. - * @out_bh is substituted only when zero is returned. - * - * Return Value: On success, it returns 0. On error, the following negative - * error code is returned. - * - * %-ENOMEM - Insufficient memory available. - * - * %-EIO - I/O error - * - * %-ENOENT - the specified block does not exist (hole block) - * - * %-EROFS - Read only filesystem (for create mode) - */ -int nilfs_mdt_get_block(struct inode *inode, unsigned long blkoff, int create, - void (*init_block)(struct inode *, - struct buffer_head *, void *), - struct buffer_head **out_bh) -{ - int ret; - - /* Should be rewritten with merging nilfs_mdt_read_block() */ - retry: - ret = nilfs_mdt_read_block(inode, blkoff, !create, out_bh); - if (!create || ret != -ENOENT) - return ret; - - ret = nilfs_mdt_create_block(inode, blkoff, out_bh, init_block); - if (unlikely(ret == -EEXIST)) { - /* create = 0; */ /* limit read-create loop retries */ - goto retry; - } - return ret; -} - -/** - * nilfs_mdt_delete_block - make a hole on the meta data file. - * @inode: inode of the meta data file - * @block: block offset - * - * Return Value: On success, zero is returned. - * On error, one of the following negative error code is returned. - * - * %-ENOMEM - Insufficient memory available. - * - * %-EIO - I/O error - */ -int nilfs_mdt_delete_block(struct inode *inode, unsigned long block) -{ - struct nilfs_inode_info *ii = NILFS_I(inode); - int err; - - err = nilfs_bmap_delete(ii->i_bmap, block); - if (!err || err == -ENOENT) { - nilfs_mdt_mark_dirty(inode); - nilfs_mdt_forget_block(inode, block); - } - return err; -} - -/** - * nilfs_mdt_forget_block - discard dirty state and try to remove the page - * @inode: inode of the meta data file - * @block: block offset - * - * nilfs_mdt_forget_block() clears a dirty flag of the specified buffer, and - * tries to release the page including the buffer from a page cache. - * - * Return Value: On success, 0 is returned. On error, one of the following - * negative error code is returned. - * - * %-EBUSY - page has an active buffer. - * - * %-ENOENT - page cache has no page addressed by the offset. - */ -int nilfs_mdt_forget_block(struct inode *inode, unsigned long block) -{ - pgoff_t index = (pgoff_t)block >> - (PAGE_CACHE_SHIFT - inode->i_blkbits); - struct page *page; - unsigned long first_block; - int ret = 0; - int still_dirty; - - page = find_lock_page(inode->i_mapping, index); - if (!page) - return -ENOENT; - - wait_on_page_writeback(page); - - first_block = (unsigned long)index << - (PAGE_CACHE_SHIFT - inode->i_blkbits); - if (page_has_buffers(page)) { - struct buffer_head *bh; - - bh = nilfs_page_get_nth_block(page, block - first_block); - nilfs_forget_buffer(bh); - } - still_dirty = PageDirty(page); - unlock_page(page); - page_cache_release(page); - - if (still_dirty || - invalidate_inode_pages2_range(inode->i_mapping, index, index) != 0) - ret = -EBUSY; - return ret; -} - -/** - * nilfs_mdt_mark_block_dirty - mark a block on the meta data file dirty. - * @inode: inode of the meta data file - * @block: block offset - * - * Return Value: On success, it returns 0. On error, the following negative - * error code is returned. - * - * %-ENOMEM - Insufficient memory available. - * - * %-EIO - I/O error - * - * %-ENOENT - the specified block does not exist (hole block) - */ -int nilfs_mdt_mark_block_dirty(struct inode *inode, unsigned long block) -{ - struct buffer_head *bh; - int err; - - err = nilfs_mdt_read_block(inode, block, 0, &bh); - if (unlikely(err)) - return err; - mark_buffer_dirty(bh); - nilfs_mdt_mark_dirty(inode); - brelse(bh); - return 0; -} - -int nilfs_mdt_fetch_dirty(struct inode *inode) -{ - struct nilfs_inode_info *ii = NILFS_I(inode); - - if (nilfs_bmap_test_and_clear_dirty(ii->i_bmap)) { - set_bit(NILFS_I_DIRTY, &ii->i_state); - return 1; - } - return test_bit(NILFS_I_DIRTY, &ii->i_state); -} - -static int -nilfs_mdt_write_page(struct page *page, struct writeback_control *wbc) -{ - struct inode *inode; - struct super_block *sb; - int err = 0; - - redirty_page_for_writepage(wbc, page); - unlock_page(page); - - inode = page->mapping->host; - if (!inode) - return 0; - - sb = inode->i_sb; - - if (wbc->sync_mode == WB_SYNC_ALL) - err = nilfs_construct_segment(sb); - else if (wbc->for_reclaim) - nilfs_flush_segment(sb, inode->i_ino); - - return err; -} - - -static const struct address_space_operations def_mdt_aops = { - .writepage = nilfs_mdt_write_page, -}; - -static const struct inode_operations def_mdt_iops; -static const struct file_operations def_mdt_fops; - - -int nilfs_mdt_init(struct inode *inode, gfp_t gfp_mask, size_t objsz) -{ - struct nilfs_mdt_info *mi; - - mi = kzalloc(max(sizeof(*mi), objsz), GFP_NOFS); - if (!mi) - return -ENOMEM; - - init_rwsem(&mi->mi_sem); - inode->i_private = mi; - - inode->i_mode = S_IFREG; - mapping_set_gfp_mask(inode->i_mapping, gfp_mask); - inode->i_mapping->backing_dev_info = inode->i_sb->s_bdi; - - inode->i_op = &def_mdt_iops; - inode->i_fop = &def_mdt_fops; - inode->i_mapping->a_ops = &def_mdt_aops; - - return 0; -} - -void nilfs_mdt_set_entry_size(struct inode *inode, unsigned entry_size, - unsigned header_size) -{ - struct nilfs_mdt_info *mi = NILFS_MDT(inode); - - mi->mi_entry_size = entry_size; - mi->mi_entries_per_block = (1 << inode->i_blkbits) / entry_size; - mi->mi_first_entry_offset = DIV_ROUND_UP(header_size, entry_size); -} - -/** - * nilfs_mdt_setup_shadow_map - setup shadow map and bind it to metadata file - * @inode: inode of the metadata file - * @shadow: shadow mapping - */ -int nilfs_mdt_setup_shadow_map(struct inode *inode, - struct nilfs_shadow_map *shadow) -{ - struct nilfs_mdt_info *mi = NILFS_MDT(inode); - struct backing_dev_info *bdi = inode->i_sb->s_bdi; - - INIT_LIST_HEAD(&shadow->frozen_buffers); - address_space_init_once(&shadow->frozen_data); - nilfs_mapping_init(&shadow->frozen_data, inode, bdi); - address_space_init_once(&shadow->frozen_btnodes); - nilfs_mapping_init(&shadow->frozen_btnodes, inode, bdi); - mi->mi_shadow = shadow; - return 0; -} - -/** - * nilfs_mdt_save_to_shadow_map - copy bmap and dirty pages to shadow map - * @inode: inode of the metadata file - */ -int nilfs_mdt_save_to_shadow_map(struct inode *inode) -{ - struct nilfs_mdt_info *mi = NILFS_MDT(inode); - struct nilfs_inode_info *ii = NILFS_I(inode); - struct nilfs_shadow_map *shadow = mi->mi_shadow; - int ret; - - ret = nilfs_copy_dirty_pages(&shadow->frozen_data, inode->i_mapping); - if (ret) - goto out; - - ret = nilfs_copy_dirty_pages(&shadow->frozen_btnodes, - &ii->i_btnode_cache); - if (ret) - goto out; - - nilfs_bmap_save(ii->i_bmap, &shadow->bmap_store); - out: - return ret; -} - -int nilfs_mdt_freeze_buffer(struct inode *inode, struct buffer_head *bh) -{ - struct nilfs_shadow_map *shadow = NILFS_MDT(inode)->mi_shadow; - struct buffer_head *bh_frozen; - struct page *page; - int blkbits = inode->i_blkbits; - - page = grab_cache_page(&shadow->frozen_data, bh->b_page->index); - if (!page) - return -ENOMEM; - - if (!page_has_buffers(page)) - create_empty_buffers(page, 1 << blkbits, 0); - - bh_frozen = nilfs_page_get_nth_block(page, bh_offset(bh) >> blkbits); - - if (!buffer_uptodate(bh_frozen)) - nilfs_copy_buffer(bh_frozen, bh); - if (list_empty(&bh_frozen->b_assoc_buffers)) { - list_add_tail(&bh_frozen->b_assoc_buffers, - &shadow->frozen_buffers); - set_buffer_nilfs_redirected(bh); - } else { - brelse(bh_frozen); /* already frozen */ - } - - unlock_page(page); - page_cache_release(page); - return 0; -} - -struct buffer_head * -nilfs_mdt_get_frozen_buffer(struct inode *inode, struct buffer_head *bh) -{ - struct nilfs_shadow_map *shadow = NILFS_MDT(inode)->mi_shadow; - struct buffer_head *bh_frozen = NULL; - struct page *page; - int n; - - page = find_lock_page(&shadow->frozen_data, bh->b_page->index); - if (page) { - if (page_has_buffers(page)) { - n = bh_offset(bh) >> inode->i_blkbits; - bh_frozen = nilfs_page_get_nth_block(page, n); - } - unlock_page(page); - page_cache_release(page); - } - return bh_frozen; -} - -static void nilfs_release_frozen_buffers(struct nilfs_shadow_map *shadow) -{ - struct list_head *head = &shadow->frozen_buffers; - struct buffer_head *bh; - - while (!list_empty(head)) { - bh = list_first_entry(head, struct buffer_head, - b_assoc_buffers); - list_del_init(&bh->b_assoc_buffers); - brelse(bh); /* drop ref-count to make it releasable */ - } -} - -/** - * nilfs_mdt_restore_from_shadow_map - restore dirty pages and bmap state - * @inode: inode of the metadata file - */ -void nilfs_mdt_restore_from_shadow_map(struct inode *inode) -{ - struct nilfs_mdt_info *mi = NILFS_MDT(inode); - struct nilfs_inode_info *ii = NILFS_I(inode); - struct nilfs_shadow_map *shadow = mi->mi_shadow; - - down_write(&mi->mi_sem); - - if (mi->mi_palloc_cache) - nilfs_palloc_clear_cache(inode); - - nilfs_clear_dirty_pages(inode->i_mapping); - nilfs_copy_back_pages(inode->i_mapping, &shadow->frozen_data); - - nilfs_clear_dirty_pages(&ii->i_btnode_cache); - nilfs_copy_back_pages(&ii->i_btnode_cache, &shadow->frozen_btnodes); - - nilfs_bmap_restore(ii->i_bmap, &shadow->bmap_store); - - up_write(&mi->mi_sem); -} - -/** - * nilfs_mdt_clear_shadow_map - truncate pages in shadow map caches - * @inode: inode of the metadata file - */ -void nilfs_mdt_clear_shadow_map(struct inode *inode) -{ - struct nilfs_mdt_info *mi = NILFS_MDT(inode); - struct nilfs_shadow_map *shadow = mi->mi_shadow; - - down_write(&mi->mi_sem); - nilfs_release_frozen_buffers(shadow); - truncate_inode_pages(&shadow->frozen_data, 0); - truncate_inode_pages(&shadow->frozen_btnodes, 0); - up_write(&mi->mi_sem); -} diff --git a/ANDROID_3.4.5/fs/nilfs2/mdt.h b/ANDROID_3.4.5/fs/nilfs2/mdt.h deleted file mode 100644 index ab20a4ba..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/mdt.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * mdt.h - NILFS meta data file prototype and definitions - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Ryusuke Konishi <ryusuke@osrg.net> - */ - -#ifndef _NILFS_MDT_H -#define _NILFS_MDT_H - -#include <linux/buffer_head.h> -#include <linux/blockgroup_lock.h> -#include "nilfs.h" -#include "page.h" - -struct nilfs_shadow_map { - struct nilfs_bmap_store bmap_store; - struct address_space frozen_data; - struct address_space frozen_btnodes; - struct list_head frozen_buffers; -}; - -/** - * struct nilfs_mdt_info - on-memory private data of meta data files - * @mi_sem: reader/writer semaphore for meta data operations - * @mi_bgl: per-blockgroup locking - * @mi_entry_size: size of an entry - * @mi_first_entry_offset: offset to the first entry - * @mi_entries_per_block: number of entries in a block - * @mi_palloc_cache: persistent object allocator cache - * @mi_shadow: shadow of bmap and page caches - * @mi_blocks_per_group: number of blocks in a group - * @mi_blocks_per_desc_block: number of blocks per descriptor block - */ -struct nilfs_mdt_info { - struct rw_semaphore mi_sem; - struct blockgroup_lock *mi_bgl; - unsigned mi_entry_size; - unsigned mi_first_entry_offset; - unsigned long mi_entries_per_block; - struct nilfs_palloc_cache *mi_palloc_cache; - struct nilfs_shadow_map *mi_shadow; - unsigned long mi_blocks_per_group; - unsigned long mi_blocks_per_desc_block; -}; - -static inline struct nilfs_mdt_info *NILFS_MDT(const struct inode *inode) -{ - return inode->i_private; -} - -/* Default GFP flags using highmem */ -#define NILFS_MDT_GFP (__GFP_WAIT | __GFP_IO | __GFP_HIGHMEM) - -int nilfs_mdt_get_block(struct inode *, unsigned long, int, - void (*init_block)(struct inode *, - struct buffer_head *, void *), - struct buffer_head **); -int nilfs_mdt_delete_block(struct inode *, unsigned long); -int nilfs_mdt_forget_block(struct inode *, unsigned long); -int nilfs_mdt_mark_block_dirty(struct inode *, unsigned long); -int nilfs_mdt_fetch_dirty(struct inode *); - -int nilfs_mdt_init(struct inode *inode, gfp_t gfp_mask, size_t objsz); -void nilfs_mdt_set_entry_size(struct inode *, unsigned, unsigned); - -int nilfs_mdt_setup_shadow_map(struct inode *inode, - struct nilfs_shadow_map *shadow); -int nilfs_mdt_save_to_shadow_map(struct inode *inode); -void nilfs_mdt_restore_from_shadow_map(struct inode *inode); -void nilfs_mdt_clear_shadow_map(struct inode *inode); -int nilfs_mdt_freeze_buffer(struct inode *inode, struct buffer_head *bh); -struct buffer_head *nilfs_mdt_get_frozen_buffer(struct inode *inode, - struct buffer_head *bh); - -static inline void nilfs_mdt_mark_dirty(struct inode *inode) -{ - if (!test_bit(NILFS_I_DIRTY, &NILFS_I(inode)->i_state)) - set_bit(NILFS_I_DIRTY, &NILFS_I(inode)->i_state); -} - -static inline void nilfs_mdt_clear_dirty(struct inode *inode) -{ - clear_bit(NILFS_I_DIRTY, &NILFS_I(inode)->i_state); -} - -static inline __u64 nilfs_mdt_cno(struct inode *inode) -{ - return ((struct the_nilfs *)inode->i_sb->s_fs_info)->ns_cno; -} - -#define nilfs_mdt_bgl_lock(inode, bg) \ - (&NILFS_MDT(inode)->mi_bgl->locks[(bg) & (NR_BG_LOCKS-1)].lock) - -#endif /* _NILFS_MDT_H */ diff --git a/ANDROID_3.4.5/fs/nilfs2/namei.c b/ANDROID_3.4.5/fs/nilfs2/namei.c deleted file mode 100644 index fce2bbee..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/namei.c +++ /dev/null @@ -1,578 +0,0 @@ -/* - * namei.c - NILFS pathname lookup operations. - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Modified for NILFS by Amagai Yoshiji <amagai@osrg.net>, - * Ryusuke Konishi <ryusuke@osrg.net> - */ -/* - * linux/fs/ext2/namei.c - * - * Copyright (C) 1992, 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) - * - * from - * - * linux/fs/minix/namei.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * - * Big-endian to little-endian byte-swapping/bitmaps by - * David S. Miller (davem@caip.rutgers.edu), 1995 - */ - -#include <linux/pagemap.h> -#include "nilfs.h" -#include "export.h" - -#define NILFS_FID_SIZE_NON_CONNECTABLE \ - (offsetof(struct nilfs_fid, parent_gen) / 4) -#define NILFS_FID_SIZE_CONNECTABLE (sizeof(struct nilfs_fid) / 4) - -static inline int nilfs_add_nondir(struct dentry *dentry, struct inode *inode) -{ - int err = nilfs_add_link(dentry, inode); - if (!err) { - d_instantiate(dentry, inode); - return 0; - } - inode_dec_link_count(inode); - iput(inode); - return err; -} - -/* - * Methods themselves. - */ - -static struct dentry * -nilfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) -{ - struct inode *inode; - ino_t ino; - - if (dentry->d_name.len > NILFS_NAME_LEN) - return ERR_PTR(-ENAMETOOLONG); - - ino = nilfs_inode_by_name(dir, &dentry->d_name); - inode = ino ? nilfs_iget(dir->i_sb, NILFS_I(dir)->i_root, ino) : NULL; - return d_splice_alias(inode, dentry); -} - -/* - * By the time this is called, we already have created - * the directory cache entry for the new file, but it - * is so far negative - it has no inode. - * - * If the create succeeds, we fill in the inode information - * with d_instantiate(). - */ -static int nilfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, - struct nameidata *nd) -{ - struct inode *inode; - struct nilfs_transaction_info ti; - int err; - - err = nilfs_transaction_begin(dir->i_sb, &ti, 1); - if (err) - return err; - inode = nilfs_new_inode(dir, mode); - err = PTR_ERR(inode); - if (!IS_ERR(inode)) { - inode->i_op = &nilfs_file_inode_operations; - inode->i_fop = &nilfs_file_operations; - inode->i_mapping->a_ops = &nilfs_aops; - nilfs_mark_inode_dirty(inode); - err = nilfs_add_nondir(dentry, inode); - } - if (!err) - err = nilfs_transaction_commit(dir->i_sb); - else - nilfs_transaction_abort(dir->i_sb); - - return err; -} - -static int -nilfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev) -{ - struct inode *inode; - struct nilfs_transaction_info ti; - int err; - - if (!new_valid_dev(rdev)) - return -EINVAL; - - err = nilfs_transaction_begin(dir->i_sb, &ti, 1); - if (err) - return err; - inode = nilfs_new_inode(dir, mode); - err = PTR_ERR(inode); - if (!IS_ERR(inode)) { - init_special_inode(inode, inode->i_mode, rdev); - nilfs_mark_inode_dirty(inode); - err = nilfs_add_nondir(dentry, inode); - } - if (!err) - err = nilfs_transaction_commit(dir->i_sb); - else - nilfs_transaction_abort(dir->i_sb); - - return err; -} - -static int nilfs_symlink(struct inode *dir, struct dentry *dentry, - const char *symname) -{ - struct nilfs_transaction_info ti; - struct super_block *sb = dir->i_sb; - unsigned l = strlen(symname)+1; - struct inode *inode; - int err; - - if (l > sb->s_blocksize) - return -ENAMETOOLONG; - - err = nilfs_transaction_begin(dir->i_sb, &ti, 1); - if (err) - return err; - - inode = nilfs_new_inode(dir, S_IFLNK | S_IRWXUGO); - err = PTR_ERR(inode); - if (IS_ERR(inode)) - goto out; - - /* slow symlink */ - inode->i_op = &nilfs_symlink_inode_operations; - inode->i_mapping->a_ops = &nilfs_aops; - err = page_symlink(inode, symname, l); - if (err) - goto out_fail; - - /* mark_inode_dirty(inode); */ - /* page_symlink() do this */ - - err = nilfs_add_nondir(dentry, inode); -out: - if (!err) - err = nilfs_transaction_commit(dir->i_sb); - else - nilfs_transaction_abort(dir->i_sb); - - return err; - -out_fail: - drop_nlink(inode); - nilfs_mark_inode_dirty(inode); - iput(inode); - goto out; -} - -static int nilfs_link(struct dentry *old_dentry, struct inode *dir, - struct dentry *dentry) -{ - struct inode *inode = old_dentry->d_inode; - struct nilfs_transaction_info ti; - int err; - - err = nilfs_transaction_begin(dir->i_sb, &ti, 1); - if (err) - return err; - - inode->i_ctime = CURRENT_TIME; - inode_inc_link_count(inode); - ihold(inode); - - err = nilfs_add_nondir(dentry, inode); - if (!err) - err = nilfs_transaction_commit(dir->i_sb); - else - nilfs_transaction_abort(dir->i_sb); - - return err; -} - -static int nilfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) -{ - struct inode *inode; - struct nilfs_transaction_info ti; - int err; - - err = nilfs_transaction_begin(dir->i_sb, &ti, 1); - if (err) - return err; - - inc_nlink(dir); - - inode = nilfs_new_inode(dir, S_IFDIR | mode); - err = PTR_ERR(inode); - if (IS_ERR(inode)) - goto out_dir; - - inode->i_op = &nilfs_dir_inode_operations; - inode->i_fop = &nilfs_dir_operations; - inode->i_mapping->a_ops = &nilfs_aops; - - inc_nlink(inode); - - err = nilfs_make_empty(inode, dir); - if (err) - goto out_fail; - - err = nilfs_add_link(dentry, inode); - if (err) - goto out_fail; - - nilfs_mark_inode_dirty(inode); - d_instantiate(dentry, inode); -out: - if (!err) - err = nilfs_transaction_commit(dir->i_sb); - else - nilfs_transaction_abort(dir->i_sb); - - return err; - -out_fail: - drop_nlink(inode); - drop_nlink(inode); - nilfs_mark_inode_dirty(inode); - iput(inode); -out_dir: - drop_nlink(dir); - nilfs_mark_inode_dirty(dir); - goto out; -} - -static int nilfs_do_unlink(struct inode *dir, struct dentry *dentry) -{ - struct inode *inode; - struct nilfs_dir_entry *de; - struct page *page; - int err; - - err = -ENOENT; - de = nilfs_find_entry(dir, &dentry->d_name, &page); - if (!de) - goto out; - - inode = dentry->d_inode; - err = -EIO; - if (le64_to_cpu(de->inode) != inode->i_ino) - goto out; - - if (!inode->i_nlink) { - nilfs_warning(inode->i_sb, __func__, - "deleting nonexistent file (%lu), %d\n", - inode->i_ino, inode->i_nlink); - set_nlink(inode, 1); - } - err = nilfs_delete_entry(de, page); - if (err) - goto out; - - inode->i_ctime = dir->i_ctime; - drop_nlink(inode); - err = 0; -out: - return err; -} - -static int nilfs_unlink(struct inode *dir, struct dentry *dentry) -{ - struct nilfs_transaction_info ti; - int err; - - err = nilfs_transaction_begin(dir->i_sb, &ti, 0); - if (err) - return err; - - err = nilfs_do_unlink(dir, dentry); - - if (!err) { - nilfs_mark_inode_dirty(dir); - nilfs_mark_inode_dirty(dentry->d_inode); - err = nilfs_transaction_commit(dir->i_sb); - } else - nilfs_transaction_abort(dir->i_sb); - - return err; -} - -static int nilfs_rmdir(struct inode *dir, struct dentry *dentry) -{ - struct inode *inode = dentry->d_inode; - struct nilfs_transaction_info ti; - int err; - - err = nilfs_transaction_begin(dir->i_sb, &ti, 0); - if (err) - return err; - - err = -ENOTEMPTY; - if (nilfs_empty_dir(inode)) { - err = nilfs_do_unlink(dir, dentry); - if (!err) { - inode->i_size = 0; - drop_nlink(inode); - nilfs_mark_inode_dirty(inode); - drop_nlink(dir); - nilfs_mark_inode_dirty(dir); - } - } - if (!err) - err = nilfs_transaction_commit(dir->i_sb); - else - nilfs_transaction_abort(dir->i_sb); - - return err; -} - -static int nilfs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - struct inode *old_inode = old_dentry->d_inode; - struct inode *new_inode = new_dentry->d_inode; - struct page *dir_page = NULL; - struct nilfs_dir_entry *dir_de = NULL; - struct page *old_page; - struct nilfs_dir_entry *old_de; - struct nilfs_transaction_info ti; - int err; - - err = nilfs_transaction_begin(old_dir->i_sb, &ti, 1); - if (unlikely(err)) - return err; - - err = -ENOENT; - old_de = nilfs_find_entry(old_dir, &old_dentry->d_name, &old_page); - if (!old_de) - goto out; - - if (S_ISDIR(old_inode->i_mode)) { - err = -EIO; - dir_de = nilfs_dotdot(old_inode, &dir_page); - if (!dir_de) - goto out_old; - } - - if (new_inode) { - struct page *new_page; - struct nilfs_dir_entry *new_de; - - err = -ENOTEMPTY; - if (dir_de && !nilfs_empty_dir(new_inode)) - goto out_dir; - - err = -ENOENT; - new_de = nilfs_find_entry(new_dir, &new_dentry->d_name, &new_page); - if (!new_de) - goto out_dir; - nilfs_set_link(new_dir, new_de, new_page, old_inode); - nilfs_mark_inode_dirty(new_dir); - new_inode->i_ctime = CURRENT_TIME; - if (dir_de) - drop_nlink(new_inode); - drop_nlink(new_inode); - nilfs_mark_inode_dirty(new_inode); - } else { - err = nilfs_add_link(new_dentry, old_inode); - if (err) - goto out_dir; - if (dir_de) { - inc_nlink(new_dir); - nilfs_mark_inode_dirty(new_dir); - } - } - - /* - * Like most other Unix systems, set the ctime for inodes on a - * rename. - */ - old_inode->i_ctime = CURRENT_TIME; - - nilfs_delete_entry(old_de, old_page); - - if (dir_de) { - nilfs_set_link(old_inode, dir_de, dir_page, new_dir); - drop_nlink(old_dir); - } - nilfs_mark_inode_dirty(old_dir); - nilfs_mark_inode_dirty(old_inode); - - err = nilfs_transaction_commit(old_dir->i_sb); - return err; - -out_dir: - if (dir_de) { - kunmap(dir_page); - page_cache_release(dir_page); - } -out_old: - kunmap(old_page); - page_cache_release(old_page); -out: - nilfs_transaction_abort(old_dir->i_sb); - return err; -} - -/* - * Export operations - */ -static struct dentry *nilfs_get_parent(struct dentry *child) -{ - unsigned long ino; - struct inode *inode; - struct qstr dotdot = {.name = "..", .len = 2}; - struct nilfs_root *root; - - ino = nilfs_inode_by_name(child->d_inode, &dotdot); - if (!ino) - return ERR_PTR(-ENOENT); - - root = NILFS_I(child->d_inode)->i_root; - - inode = nilfs_iget(child->d_inode->i_sb, root, ino); - if (IS_ERR(inode)) - return ERR_CAST(inode); - - return d_obtain_alias(inode); -} - -static struct dentry *nilfs_get_dentry(struct super_block *sb, u64 cno, - u64 ino, u32 gen) -{ - struct nilfs_root *root; - struct inode *inode; - - if (ino < NILFS_FIRST_INO(sb) && ino != NILFS_ROOT_INO) - return ERR_PTR(-ESTALE); - - root = nilfs_lookup_root(sb->s_fs_info, cno); - if (!root) - return ERR_PTR(-ESTALE); - - inode = nilfs_iget(sb, root, ino); - nilfs_put_root(root); - - if (IS_ERR(inode)) - return ERR_CAST(inode); - if (gen && inode->i_generation != gen) { - iput(inode); - return ERR_PTR(-ESTALE); - } - return d_obtain_alias(inode); -} - -static struct dentry *nilfs_fh_to_dentry(struct super_block *sb, struct fid *fh, - int fh_len, int fh_type) -{ - struct nilfs_fid *fid = (struct nilfs_fid *)fh; - - if ((fh_len != NILFS_FID_SIZE_NON_CONNECTABLE && - fh_len != NILFS_FID_SIZE_CONNECTABLE) || - (fh_type != FILEID_NILFS_WITH_PARENT && - fh_type != FILEID_NILFS_WITHOUT_PARENT)) - return NULL; - - return nilfs_get_dentry(sb, fid->cno, fid->ino, fid->gen); -} - -static struct dentry *nilfs_fh_to_parent(struct super_block *sb, struct fid *fh, - int fh_len, int fh_type) -{ - struct nilfs_fid *fid = (struct nilfs_fid *)fh; - - if (fh_len != NILFS_FID_SIZE_CONNECTABLE || - fh_type != FILEID_NILFS_WITH_PARENT) - return NULL; - - return nilfs_get_dentry(sb, fid->cno, fid->parent_ino, fid->parent_gen); -} - -static int nilfs_encode_fh(struct dentry *dentry, __u32 *fh, int *lenp, - int connectable) -{ - struct nilfs_fid *fid = (struct nilfs_fid *)fh; - struct inode *inode = dentry->d_inode; - struct nilfs_root *root = NILFS_I(inode)->i_root; - int type; - - if (*lenp < NILFS_FID_SIZE_NON_CONNECTABLE || - (connectable && *lenp < NILFS_FID_SIZE_CONNECTABLE)) - return 255; - - fid->cno = root->cno; - fid->ino = inode->i_ino; - fid->gen = inode->i_generation; - - if (connectable && !S_ISDIR(inode->i_mode)) { - struct inode *parent; - - spin_lock(&dentry->d_lock); - parent = dentry->d_parent->d_inode; - fid->parent_ino = parent->i_ino; - fid->parent_gen = parent->i_generation; - spin_unlock(&dentry->d_lock); - - type = FILEID_NILFS_WITH_PARENT; - *lenp = NILFS_FID_SIZE_CONNECTABLE; - } else { - type = FILEID_NILFS_WITHOUT_PARENT; - *lenp = NILFS_FID_SIZE_NON_CONNECTABLE; - } - - return type; -} - -const struct inode_operations nilfs_dir_inode_operations = { - .create = nilfs_create, - .lookup = nilfs_lookup, - .link = nilfs_link, - .unlink = nilfs_unlink, - .symlink = nilfs_symlink, - .mkdir = nilfs_mkdir, - .rmdir = nilfs_rmdir, - .mknod = nilfs_mknod, - .rename = nilfs_rename, - .setattr = nilfs_setattr, - .permission = nilfs_permission, - .fiemap = nilfs_fiemap, -}; - -const struct inode_operations nilfs_special_inode_operations = { - .setattr = nilfs_setattr, - .permission = nilfs_permission, -}; - -const struct inode_operations nilfs_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, - .permission = nilfs_permission, -}; - -const struct export_operations nilfs_export_ops = { - .encode_fh = nilfs_encode_fh, - .fh_to_dentry = nilfs_fh_to_dentry, - .fh_to_parent = nilfs_fh_to_parent, - .get_parent = nilfs_get_parent, -}; diff --git a/ANDROID_3.4.5/fs/nilfs2/nilfs.h b/ANDROID_3.4.5/fs/nilfs2/nilfs.h deleted file mode 100644 index 250add84..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/nilfs.h +++ /dev/null @@ -1,326 +0,0 @@ -/* - * nilfs.h - NILFS local header file. - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Koji Sato <koji@osrg.net> - * Ryusuke Konishi <ryusuke@osrg.net> - */ - -#ifndef _NILFS_H -#define _NILFS_H - -#include <linux/kernel.h> -#include <linux/buffer_head.h> -#include <linux/spinlock.h> -#include <linux/blkdev.h> -#include <linux/nilfs2_fs.h> -#include "the_nilfs.h" -#include "bmap.h" - -/* - * nilfs inode data in memory - */ -struct nilfs_inode_info { - __u32 i_flags; - unsigned long i_state; /* Dynamic state flags */ - struct nilfs_bmap *i_bmap; - struct nilfs_bmap i_bmap_data; - __u64 i_xattr; /* sector_t ??? */ - __u32 i_dir_start_lookup; - __u64 i_cno; /* check point number for GC inode */ - struct address_space i_btnode_cache; - struct list_head i_dirty; /* List for connecting dirty files */ - -#ifdef CONFIG_NILFS_XATTR - /* - * Extended attributes can be read independently of the main file - * data. Taking i_sem even when reading would cause contention - * between readers of EAs and writers of regular file data, so - * instead we synchronize on xattr_sem when reading or changing - * EAs. - */ - struct rw_semaphore xattr_sem; -#endif - struct buffer_head *i_bh; /* i_bh contains a new or dirty - disk inode */ - struct nilfs_root *i_root; - struct inode vfs_inode; -}; - -static inline struct nilfs_inode_info *NILFS_I(const struct inode *inode) -{ - return container_of(inode, struct nilfs_inode_info, vfs_inode); -} - -static inline struct nilfs_inode_info * -NILFS_BMAP_I(const struct nilfs_bmap *bmap) -{ - return container_of(bmap, struct nilfs_inode_info, i_bmap_data); -} - -static inline struct inode *NILFS_BTNC_I(struct address_space *btnc) -{ - struct nilfs_inode_info *ii = - container_of(btnc, struct nilfs_inode_info, i_btnode_cache); - return &ii->vfs_inode; -} - -/* - * Dynamic state flags of NILFS on-memory inode (i_state) - */ -enum { - NILFS_I_NEW = 0, /* Inode is newly created */ - NILFS_I_DIRTY, /* The file is dirty */ - NILFS_I_QUEUED, /* inode is in dirty_files list */ - NILFS_I_BUSY, /* inode is grabbed by a segment - constructor */ - NILFS_I_COLLECTED, /* All dirty blocks are collected */ - NILFS_I_UPDATED, /* The file has been written back */ - NILFS_I_INODE_DIRTY, /* write_inode is requested */ - NILFS_I_BMAP, /* has bmap and btnode_cache */ - NILFS_I_GCINODE, /* inode for GC, on memory only */ -}; - -/* - * commit flags for nilfs_commit_super and nilfs_sync_super - */ -enum { - NILFS_SB_COMMIT = 0, /* Commit a super block alternately */ - NILFS_SB_COMMIT_ALL /* Commit both super blocks */ -}; - -/* - * Macros to check inode numbers - */ -#define NILFS_MDT_INO_BITS \ - ((unsigned int)(1 << NILFS_DAT_INO | 1 << NILFS_CPFILE_INO | \ - 1 << NILFS_SUFILE_INO | 1 << NILFS_IFILE_INO | \ - 1 << NILFS_ATIME_INO | 1 << NILFS_SKETCH_INO)) - -#define NILFS_SYS_INO_BITS \ - ((unsigned int)(1 << NILFS_ROOT_INO) | NILFS_MDT_INO_BITS) - -#define NILFS_FIRST_INO(sb) (((struct the_nilfs *)sb->s_fs_info)->ns_first_ino) - -#define NILFS_MDT_INODE(sb, ino) \ - ((ino) < NILFS_FIRST_INO(sb) && (NILFS_MDT_INO_BITS & (1 << (ino)))) -#define NILFS_VALID_INODE(sb, ino) \ - ((ino) >= NILFS_FIRST_INO(sb) || (NILFS_SYS_INO_BITS & (1 << (ino)))) - -/** - * struct nilfs_transaction_info: context information for synchronization - * @ti_magic: Magic number - * @ti_save: Backup of journal_info field of task_struct - * @ti_flags: Flags - * @ti_count: Nest level - * @ti_garbage: List of inode to be put when releasing semaphore - */ -struct nilfs_transaction_info { - u32 ti_magic; - void *ti_save; - /* This should never used. If this happens, - one of other filesystems has a bug. */ - unsigned short ti_flags; - unsigned short ti_count; - struct list_head ti_garbage; -}; - -/* ti_magic */ -#define NILFS_TI_MAGIC 0xd9e392fb - -/* ti_flags */ -#define NILFS_TI_DYNAMIC_ALLOC 0x0001 /* Allocated from slab */ -#define NILFS_TI_SYNC 0x0002 /* Force to construct segment at the - end of transaction. */ -#define NILFS_TI_GC 0x0004 /* GC context */ -#define NILFS_TI_COMMIT 0x0008 /* Change happened or not */ -#define NILFS_TI_WRITER 0x0010 /* Constructor context */ - - -int nilfs_transaction_begin(struct super_block *, - struct nilfs_transaction_info *, int); -int nilfs_transaction_commit(struct super_block *); -void nilfs_transaction_abort(struct super_block *); - -static inline void nilfs_set_transaction_flag(unsigned int flag) -{ - struct nilfs_transaction_info *ti = current->journal_info; - - ti->ti_flags |= flag; -} - -static inline int nilfs_test_transaction_flag(unsigned int flag) -{ - struct nilfs_transaction_info *ti = current->journal_info; - - if (ti == NULL || ti->ti_magic != NILFS_TI_MAGIC) - return 0; - return !!(ti->ti_flags & flag); -} - -static inline int nilfs_doing_gc(void) -{ - return nilfs_test_transaction_flag(NILFS_TI_GC); -} - -static inline int nilfs_doing_construction(void) -{ - return nilfs_test_transaction_flag(NILFS_TI_WRITER); -} - -/* - * function prototype - */ -#ifdef CONFIG_NILFS_POSIX_ACL -#error "NILFS: not yet supported POSIX ACL" -extern int nilfs_acl_chmod(struct inode *); -extern int nilfs_init_acl(struct inode *, struct inode *); -#else -static inline int nilfs_acl_chmod(struct inode *inode) -{ - return 0; -} - -static inline int nilfs_init_acl(struct inode *inode, struct inode *dir) -{ - inode->i_mode &= ~current_umask(); - return 0; -} -#endif - -#define NILFS_ATIME_DISABLE - -/* Flags that should be inherited by new inodes from their parent. */ -#define NILFS_FL_INHERITED \ - (FS_SECRM_FL | FS_UNRM_FL | FS_COMPR_FL | FS_SYNC_FL | \ - FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL | FS_NOATIME_FL |\ - FS_COMPRBLK_FL | FS_NOCOMP_FL | FS_NOTAIL_FL | FS_DIRSYNC_FL) - -/* Mask out flags that are inappropriate for the given type of inode. */ -static inline __u32 nilfs_mask_flags(umode_t mode, __u32 flags) -{ - if (S_ISDIR(mode)) - return flags; - else if (S_ISREG(mode)) - return flags & ~(FS_DIRSYNC_FL | FS_TOPDIR_FL); - else - return flags & (FS_NODUMP_FL | FS_NOATIME_FL); -} - -/* dir.c */ -extern int nilfs_add_link(struct dentry *, struct inode *); -extern ino_t nilfs_inode_by_name(struct inode *, const struct qstr *); -extern int nilfs_make_empty(struct inode *, struct inode *); -extern struct nilfs_dir_entry * -nilfs_find_entry(struct inode *, const struct qstr *, struct page **); -extern int nilfs_delete_entry(struct nilfs_dir_entry *, struct page *); -extern int nilfs_empty_dir(struct inode *); -extern struct nilfs_dir_entry *nilfs_dotdot(struct inode *, struct page **); -extern void nilfs_set_link(struct inode *, struct nilfs_dir_entry *, - struct page *, struct inode *); - -/* file.c */ -extern int nilfs_sync_file(struct file *, loff_t, loff_t, int); - -/* ioctl.c */ -long nilfs_ioctl(struct file *, unsigned int, unsigned long); -long nilfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *, struct nilfs_argv *, - void **); - -/* inode.c */ -void nilfs_inode_add_blocks(struct inode *inode, int n); -void nilfs_inode_sub_blocks(struct inode *inode, int n); -extern struct inode *nilfs_new_inode(struct inode *, umode_t); -extern void nilfs_free_inode(struct inode *); -extern int nilfs_get_block(struct inode *, sector_t, struct buffer_head *, int); -extern void nilfs_set_inode_flags(struct inode *); -extern int nilfs_read_inode_common(struct inode *, struct nilfs_inode *); -extern void nilfs_write_inode_common(struct inode *, struct nilfs_inode *, int); -struct inode *nilfs_ilookup(struct super_block *sb, struct nilfs_root *root, - unsigned long ino); -struct inode *nilfs_iget_locked(struct super_block *sb, struct nilfs_root *root, - unsigned long ino); -struct inode *nilfs_iget(struct super_block *sb, struct nilfs_root *root, - unsigned long ino); -extern struct inode *nilfs_iget_for_gc(struct super_block *sb, - unsigned long ino, __u64 cno); -extern void nilfs_update_inode(struct inode *, struct buffer_head *); -extern void nilfs_truncate(struct inode *); -extern void nilfs_evict_inode(struct inode *); -extern int nilfs_setattr(struct dentry *, struct iattr *); -int nilfs_permission(struct inode *inode, int mask); -int nilfs_load_inode_block(struct inode *inode, struct buffer_head **pbh); -extern int nilfs_inode_dirty(struct inode *); -int nilfs_set_file_dirty(struct inode *inode, unsigned nr_dirty); -extern int nilfs_mark_inode_dirty(struct inode *); -extern void nilfs_dirty_inode(struct inode *, int flags); -int nilfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, - __u64 start, __u64 len); - -/* super.c */ -extern struct inode *nilfs_alloc_inode(struct super_block *); -extern void nilfs_destroy_inode(struct inode *); -extern __printf(3, 4) -void nilfs_error(struct super_block *, const char *, const char *, ...); -extern __printf(3, 4) -void nilfs_warning(struct super_block *, const char *, const char *, ...); -extern struct nilfs_super_block * -nilfs_read_super_block(struct super_block *, u64, int, struct buffer_head **); -extern int nilfs_store_magic_and_option(struct super_block *, - struct nilfs_super_block *, char *); -extern int nilfs_check_feature_compatibility(struct super_block *, - struct nilfs_super_block *); -extern void nilfs_set_log_cursor(struct nilfs_super_block *, - struct the_nilfs *); -struct nilfs_super_block **nilfs_prepare_super(struct super_block *sb, - int flip); -int nilfs_commit_super(struct super_block *sb, int flag); -int nilfs_cleanup_super(struct super_block *sb); -int nilfs_resize_fs(struct super_block *sb, __u64 newsize); -int nilfs_attach_checkpoint(struct super_block *sb, __u64 cno, int curr_mnt, - struct nilfs_root **root); -int nilfs_checkpoint_is_mounted(struct super_block *sb, __u64 cno); - -/* gcinode.c */ -int nilfs_gccache_submit_read_data(struct inode *, sector_t, sector_t, __u64, - struct buffer_head **); -int nilfs_gccache_submit_read_node(struct inode *, sector_t, __u64, - struct buffer_head **); -int nilfs_gccache_wait_and_mark_dirty(struct buffer_head *); -int nilfs_init_gcinode(struct inode *inode); -void nilfs_remove_all_gcinodes(struct the_nilfs *nilfs); - -/* - * Inodes and files operations - */ -extern const struct file_operations nilfs_dir_operations; -extern const struct inode_operations nilfs_file_inode_operations; -extern const struct file_operations nilfs_file_operations; -extern const struct address_space_operations nilfs_aops; -extern const struct inode_operations nilfs_dir_inode_operations; -extern const struct inode_operations nilfs_special_inode_operations; -extern const struct inode_operations nilfs_symlink_inode_operations; - -/* - * filesystem type - */ -extern struct file_system_type nilfs_fs_type; - - -#endif /* _NILFS_H */ diff --git a/ANDROID_3.4.5/fs/nilfs2/page.c b/ANDROID_3.4.5/fs/nilfs2/page.c deleted file mode 100644 index 3e7b2a0d..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/page.c +++ /dev/null @@ -1,551 +0,0 @@ -/* - * page.c - buffer/page management specific to NILFS - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Ryusuke Konishi <ryusuke@osrg.net>, - * Seiji Kihara <kihara@osrg.net>. - */ - -#include <linux/pagemap.h> -#include <linux/writeback.h> -#include <linux/swap.h> -#include <linux/bitops.h> -#include <linux/page-flags.h> -#include <linux/list.h> -#include <linux/highmem.h> -#include <linux/pagevec.h> -#include <linux/gfp.h> -#include "nilfs.h" -#include "page.h" -#include "mdt.h" - - -#define NILFS_BUFFER_INHERENT_BITS \ - ((1UL << BH_Uptodate) | (1UL << BH_Mapped) | (1UL << BH_NILFS_Node) | \ - (1UL << BH_NILFS_Volatile) | (1UL << BH_NILFS_Checked)) - -static struct buffer_head * -__nilfs_get_page_block(struct page *page, unsigned long block, pgoff_t index, - int blkbits, unsigned long b_state) - -{ - unsigned long first_block; - struct buffer_head *bh; - - if (!page_has_buffers(page)) - create_empty_buffers(page, 1 << blkbits, b_state); - - first_block = (unsigned long)index << (PAGE_CACHE_SHIFT - blkbits); - bh = nilfs_page_get_nth_block(page, block - first_block); - - touch_buffer(bh); - wait_on_buffer(bh); - return bh; -} - -struct buffer_head *nilfs_grab_buffer(struct inode *inode, - struct address_space *mapping, - unsigned long blkoff, - unsigned long b_state) -{ - int blkbits = inode->i_blkbits; - pgoff_t index = blkoff >> (PAGE_CACHE_SHIFT - blkbits); - struct page *page; - struct buffer_head *bh; - - page = grab_cache_page(mapping, index); - if (unlikely(!page)) - return NULL; - - bh = __nilfs_get_page_block(page, blkoff, index, blkbits, b_state); - if (unlikely(!bh)) { - unlock_page(page); - page_cache_release(page); - return NULL; - } - return bh; -} - -/** - * nilfs_forget_buffer - discard dirty state - * @inode: owner inode of the buffer - * @bh: buffer head of the buffer to be discarded - */ -void nilfs_forget_buffer(struct buffer_head *bh) -{ - struct page *page = bh->b_page; - - lock_buffer(bh); - clear_buffer_nilfs_volatile(bh); - clear_buffer_nilfs_checked(bh); - clear_buffer_nilfs_redirected(bh); - clear_buffer_dirty(bh); - if (nilfs_page_buffers_clean(page)) - __nilfs_clear_page_dirty(page); - - clear_buffer_uptodate(bh); - clear_buffer_mapped(bh); - bh->b_blocknr = -1; - ClearPageUptodate(page); - ClearPageMappedToDisk(page); - unlock_buffer(bh); - brelse(bh); -} - -/** - * nilfs_copy_buffer -- copy buffer data and flags - * @dbh: destination buffer - * @sbh: source buffer - */ -void nilfs_copy_buffer(struct buffer_head *dbh, struct buffer_head *sbh) -{ - void *kaddr0, *kaddr1; - unsigned long bits; - struct page *spage = sbh->b_page, *dpage = dbh->b_page; - struct buffer_head *bh; - - kaddr0 = kmap_atomic(spage); - kaddr1 = kmap_atomic(dpage); - memcpy(kaddr1 + bh_offset(dbh), kaddr0 + bh_offset(sbh), sbh->b_size); - kunmap_atomic(kaddr1); - kunmap_atomic(kaddr0); - - dbh->b_state = sbh->b_state & NILFS_BUFFER_INHERENT_BITS; - dbh->b_blocknr = sbh->b_blocknr; - dbh->b_bdev = sbh->b_bdev; - - bh = dbh; - bits = sbh->b_state & ((1UL << BH_Uptodate) | (1UL << BH_Mapped)); - while ((bh = bh->b_this_page) != dbh) { - lock_buffer(bh); - bits &= bh->b_state; - unlock_buffer(bh); - } - if (bits & (1UL << BH_Uptodate)) - SetPageUptodate(dpage); - else - ClearPageUptodate(dpage); - if (bits & (1UL << BH_Mapped)) - SetPageMappedToDisk(dpage); - else - ClearPageMappedToDisk(dpage); -} - -/** - * nilfs_page_buffers_clean - check if a page has dirty buffers or not. - * @page: page to be checked - * - * nilfs_page_buffers_clean() returns zero if the page has dirty buffers. - * Otherwise, it returns non-zero value. - */ -int nilfs_page_buffers_clean(struct page *page) -{ - struct buffer_head *bh, *head; - - bh = head = page_buffers(page); - do { - if (buffer_dirty(bh)) - return 0; - bh = bh->b_this_page; - } while (bh != head); - return 1; -} - -void nilfs_page_bug(struct page *page) -{ - struct address_space *m; - unsigned long ino; - - if (unlikely(!page)) { - printk(KERN_CRIT "NILFS_PAGE_BUG(NULL)\n"); - return; - } - - m = page->mapping; - ino = m ? m->host->i_ino : 0; - - printk(KERN_CRIT "NILFS_PAGE_BUG(%p): cnt=%d index#=%llu flags=0x%lx " - "mapping=%p ino=%lu\n", - page, atomic_read(&page->_count), - (unsigned long long)page->index, page->flags, m, ino); - - if (page_has_buffers(page)) { - struct buffer_head *bh, *head; - int i = 0; - - bh = head = page_buffers(page); - do { - printk(KERN_CRIT - " BH[%d] %p: cnt=%d block#=%llu state=0x%lx\n", - i++, bh, atomic_read(&bh->b_count), - (unsigned long long)bh->b_blocknr, bh->b_state); - bh = bh->b_this_page; - } while (bh != head); - } -} - -/** - * nilfs_copy_page -- copy the page with buffers - * @dst: destination page - * @src: source page - * @copy_dirty: flag whether to copy dirty states on the page's buffer heads. - * - * This function is for both data pages and btnode pages. The dirty flag - * should be treated by caller. The page must not be under i/o. - * Both src and dst page must be locked - */ -static void nilfs_copy_page(struct page *dst, struct page *src, int copy_dirty) -{ - struct buffer_head *dbh, *dbufs, *sbh, *sbufs; - unsigned long mask = NILFS_BUFFER_INHERENT_BITS; - - BUG_ON(PageWriteback(dst)); - - sbh = sbufs = page_buffers(src); - if (!page_has_buffers(dst)) - create_empty_buffers(dst, sbh->b_size, 0); - - if (copy_dirty) - mask |= (1UL << BH_Dirty); - - dbh = dbufs = page_buffers(dst); - do { - lock_buffer(sbh); - lock_buffer(dbh); - dbh->b_state = sbh->b_state & mask; - dbh->b_blocknr = sbh->b_blocknr; - dbh->b_bdev = sbh->b_bdev; - sbh = sbh->b_this_page; - dbh = dbh->b_this_page; - } while (dbh != dbufs); - - copy_highpage(dst, src); - - if (PageUptodate(src) && !PageUptodate(dst)) - SetPageUptodate(dst); - else if (!PageUptodate(src) && PageUptodate(dst)) - ClearPageUptodate(dst); - if (PageMappedToDisk(src) && !PageMappedToDisk(dst)) - SetPageMappedToDisk(dst); - else if (!PageMappedToDisk(src) && PageMappedToDisk(dst)) - ClearPageMappedToDisk(dst); - - do { - unlock_buffer(sbh); - unlock_buffer(dbh); - sbh = sbh->b_this_page; - dbh = dbh->b_this_page; - } while (dbh != dbufs); -} - -int nilfs_copy_dirty_pages(struct address_space *dmap, - struct address_space *smap) -{ - struct pagevec pvec; - unsigned int i; - pgoff_t index = 0; - int err = 0; - - pagevec_init(&pvec, 0); -repeat: - if (!pagevec_lookup_tag(&pvec, smap, &index, PAGECACHE_TAG_DIRTY, - PAGEVEC_SIZE)) - return 0; - - for (i = 0; i < pagevec_count(&pvec); i++) { - struct page *page = pvec.pages[i], *dpage; - - lock_page(page); - if (unlikely(!PageDirty(page))) - NILFS_PAGE_BUG(page, "inconsistent dirty state"); - - dpage = grab_cache_page(dmap, page->index); - if (unlikely(!dpage)) { - /* No empty page is added to the page cache */ - err = -ENOMEM; - unlock_page(page); - break; - } - if (unlikely(!page_has_buffers(page))) - NILFS_PAGE_BUG(page, - "found empty page in dat page cache"); - - nilfs_copy_page(dpage, page, 1); - __set_page_dirty_nobuffers(dpage); - - unlock_page(dpage); - page_cache_release(dpage); - unlock_page(page); - } - pagevec_release(&pvec); - cond_resched(); - - if (likely(!err)) - goto repeat; - return err; -} - -/** - * nilfs_copy_back_pages -- copy back pages to original cache from shadow cache - * @dmap: destination page cache - * @smap: source page cache - * - * No pages must no be added to the cache during this process. - * This must be ensured by the caller. - */ -void nilfs_copy_back_pages(struct address_space *dmap, - struct address_space *smap) -{ - struct pagevec pvec; - unsigned int i, n; - pgoff_t index = 0; - int err; - - pagevec_init(&pvec, 0); -repeat: - n = pagevec_lookup(&pvec, smap, index, PAGEVEC_SIZE); - if (!n) - return; - index = pvec.pages[n - 1]->index + 1; - - for (i = 0; i < pagevec_count(&pvec); i++) { - struct page *page = pvec.pages[i], *dpage; - pgoff_t offset = page->index; - - lock_page(page); - dpage = find_lock_page(dmap, offset); - if (dpage) { - /* override existing page on the destination cache */ - WARN_ON(PageDirty(dpage)); - nilfs_copy_page(dpage, page, 0); - unlock_page(dpage); - page_cache_release(dpage); - } else { - struct page *page2; - - /* move the page to the destination cache */ - spin_lock_irq(&smap->tree_lock); - page2 = radix_tree_delete(&smap->page_tree, offset); - WARN_ON(page2 != page); - - smap->nrpages--; - spin_unlock_irq(&smap->tree_lock); - - spin_lock_irq(&dmap->tree_lock); - err = radix_tree_insert(&dmap->page_tree, offset, page); - if (unlikely(err < 0)) { - WARN_ON(err == -EEXIST); - page->mapping = NULL; - page_cache_release(page); /* for cache */ - } else { - page->mapping = dmap; - dmap->nrpages++; - if (PageDirty(page)) - radix_tree_tag_set(&dmap->page_tree, - offset, - PAGECACHE_TAG_DIRTY); - } - spin_unlock_irq(&dmap->tree_lock); - } - unlock_page(page); - } - pagevec_release(&pvec); - cond_resched(); - - goto repeat; -} - -void nilfs_clear_dirty_pages(struct address_space *mapping) -{ - struct pagevec pvec; - unsigned int i; - pgoff_t index = 0; - - pagevec_init(&pvec, 0); - - while (pagevec_lookup_tag(&pvec, mapping, &index, PAGECACHE_TAG_DIRTY, - PAGEVEC_SIZE)) { - for (i = 0; i < pagevec_count(&pvec); i++) { - struct page *page = pvec.pages[i]; - struct buffer_head *bh, *head; - - lock_page(page); - ClearPageUptodate(page); - ClearPageMappedToDisk(page); - bh = head = page_buffers(page); - do { - lock_buffer(bh); - clear_buffer_dirty(bh); - clear_buffer_nilfs_volatile(bh); - clear_buffer_nilfs_checked(bh); - clear_buffer_nilfs_redirected(bh); - clear_buffer_uptodate(bh); - clear_buffer_mapped(bh); - unlock_buffer(bh); - bh = bh->b_this_page; - } while (bh != head); - - __nilfs_clear_page_dirty(page); - unlock_page(page); - } - pagevec_release(&pvec); - cond_resched(); - } -} - -unsigned nilfs_page_count_clean_buffers(struct page *page, - unsigned from, unsigned to) -{ - unsigned block_start, block_end; - struct buffer_head *bh, *head; - unsigned nc = 0; - - for (bh = head = page_buffers(page), block_start = 0; - bh != head || !block_start; - block_start = block_end, bh = bh->b_this_page) { - block_end = block_start + bh->b_size; - if (block_end > from && block_start < to && !buffer_dirty(bh)) - nc++; - } - return nc; -} - -void nilfs_mapping_init(struct address_space *mapping, struct inode *inode, - struct backing_dev_info *bdi) -{ - mapping->host = inode; - mapping->flags = 0; - mapping_set_gfp_mask(mapping, GFP_NOFS); - mapping->assoc_mapping = NULL; - mapping->backing_dev_info = bdi; - mapping->a_ops = &empty_aops; -} - -/* - * NILFS2 needs clear_page_dirty() in the following two cases: - * - * 1) For B-tree node pages and data pages of the dat/gcdat, NILFS2 clears - * page dirty flags when it copies back pages from the shadow cache - * (gcdat->{i_mapping,i_btnode_cache}) to its original cache - * (dat->{i_mapping,i_btnode_cache}). - * - * 2) Some B-tree operations like insertion or deletion may dispose buffers - * in dirty state, and this needs to cancel the dirty state of their pages. - */ -int __nilfs_clear_page_dirty(struct page *page) -{ - struct address_space *mapping = page->mapping; - - if (mapping) { - spin_lock_irq(&mapping->tree_lock); - if (test_bit(PG_dirty, &page->flags)) { - radix_tree_tag_clear(&mapping->page_tree, - page_index(page), - PAGECACHE_TAG_DIRTY); - spin_unlock_irq(&mapping->tree_lock); - return clear_page_dirty_for_io(page); - } - spin_unlock_irq(&mapping->tree_lock); - return 0; - } - return TestClearPageDirty(page); -} - -/** - * nilfs_find_uncommitted_extent - find extent of uncommitted data - * @inode: inode - * @start_blk: start block offset (in) - * @blkoff: start offset of the found extent (out) - * - * This function searches an extent of buffers marked "delayed" which - * starts from a block offset equal to or larger than @start_blk. If - * such an extent was found, this will store the start offset in - * @blkoff and return its length in blocks. Otherwise, zero is - * returned. - */ -unsigned long nilfs_find_uncommitted_extent(struct inode *inode, - sector_t start_blk, - sector_t *blkoff) -{ - unsigned int i; - pgoff_t index; - unsigned int nblocks_in_page; - unsigned long length = 0; - sector_t b; - struct pagevec pvec; - struct page *page; - - if (inode->i_mapping->nrpages == 0) - return 0; - - index = start_blk >> (PAGE_CACHE_SHIFT - inode->i_blkbits); - nblocks_in_page = 1U << (PAGE_CACHE_SHIFT - inode->i_blkbits); - - pagevec_init(&pvec, 0); - -repeat: - pvec.nr = find_get_pages_contig(inode->i_mapping, index, PAGEVEC_SIZE, - pvec.pages); - if (pvec.nr == 0) - return length; - - if (length > 0 && pvec.pages[0]->index > index) - goto out; - - b = pvec.pages[0]->index << (PAGE_CACHE_SHIFT - inode->i_blkbits); - i = 0; - do { - page = pvec.pages[i]; - - lock_page(page); - if (page_has_buffers(page)) { - struct buffer_head *bh, *head; - - bh = head = page_buffers(page); - do { - if (b < start_blk) - continue; - if (buffer_delay(bh)) { - if (length == 0) - *blkoff = b; - length++; - } else if (length > 0) { - goto out_locked; - } - } while (++b, bh = bh->b_this_page, bh != head); - } else { - if (length > 0) - goto out_locked; - - b += nblocks_in_page; - } - unlock_page(page); - - } while (++i < pagevec_count(&pvec)); - - index = page->index + 1; - pagevec_release(&pvec); - cond_resched(); - goto repeat; - -out_locked: - unlock_page(page); -out: - pagevec_release(&pvec); - return length; -} diff --git a/ANDROID_3.4.5/fs/nilfs2/page.h b/ANDROID_3.4.5/fs/nilfs2/page.h deleted file mode 100644 index fb7de716..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/page.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * page.h - buffer/page management specific to NILFS - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Ryusuke Konishi <ryusuke@osrg.net>, - * Seiji Kihara <kihara@osrg.net>. - */ - -#ifndef _NILFS_PAGE_H -#define _NILFS_PAGE_H - -#include <linux/buffer_head.h> -#include "nilfs.h" - -/* - * Extended buffer state bits - */ -enum { - BH_NILFS_Allocated = BH_PrivateStart, - BH_NILFS_Node, - BH_NILFS_Volatile, - BH_NILFS_Checked, - BH_NILFS_Redirected, -}; - -BUFFER_FNS(NILFS_Node, nilfs_node) /* nilfs node buffers */ -BUFFER_FNS(NILFS_Volatile, nilfs_volatile) -BUFFER_FNS(NILFS_Checked, nilfs_checked) /* buffer is verified */ -BUFFER_FNS(NILFS_Redirected, nilfs_redirected) /* redirected to a copy */ - - -int __nilfs_clear_page_dirty(struct page *); - -struct buffer_head *nilfs_grab_buffer(struct inode *, struct address_space *, - unsigned long, unsigned long); -void nilfs_forget_buffer(struct buffer_head *); -void nilfs_copy_buffer(struct buffer_head *, struct buffer_head *); -int nilfs_page_buffers_clean(struct page *); -void nilfs_page_bug(struct page *); - -int nilfs_copy_dirty_pages(struct address_space *, struct address_space *); -void nilfs_copy_back_pages(struct address_space *, struct address_space *); -void nilfs_clear_dirty_pages(struct address_space *); -void nilfs_mapping_init(struct address_space *mapping, struct inode *inode, - struct backing_dev_info *bdi); -unsigned nilfs_page_count_clean_buffers(struct page *, unsigned, unsigned); -unsigned long nilfs_find_uncommitted_extent(struct inode *inode, - sector_t start_blk, - sector_t *blkoff); - -#define NILFS_PAGE_BUG(page, m, a...) \ - do { nilfs_page_bug(page); BUG(); } while (0) - -static inline struct buffer_head * -nilfs_page_get_nth_block(struct page *page, unsigned int count) -{ - struct buffer_head *bh = page_buffers(page); - - while (count-- > 0) - bh = bh->b_this_page; - get_bh(bh); - return bh; -} - -#endif /* _NILFS_PAGE_H */ diff --git a/ANDROID_3.4.5/fs/nilfs2/recovery.c b/ANDROID_3.4.5/fs/nilfs2/recovery.c deleted file mode 100644 index f1626f50..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/recovery.c +++ /dev/null @@ -1,963 +0,0 @@ -/* - * recovery.c - NILFS recovery logic - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Ryusuke Konishi <ryusuke@osrg.net> - */ - -#include <linux/buffer_head.h> -#include <linux/blkdev.h> -#include <linux/swap.h> -#include <linux/slab.h> -#include <linux/crc32.h> -#include "nilfs.h" -#include "segment.h" -#include "sufile.h" -#include "page.h" -#include "segbuf.h" - -/* - * Segment check result - */ -enum { - NILFS_SEG_VALID, - NILFS_SEG_NO_SUPER_ROOT, - NILFS_SEG_FAIL_IO, - NILFS_SEG_FAIL_MAGIC, - NILFS_SEG_FAIL_SEQ, - NILFS_SEG_FAIL_CHECKSUM_SUPER_ROOT, - NILFS_SEG_FAIL_CHECKSUM_FULL, - NILFS_SEG_FAIL_CONSISTENCY, -}; - -/* work structure for recovery */ -struct nilfs_recovery_block { - ino_t ino; /* Inode number of the file that this block - belongs to */ - sector_t blocknr; /* block number */ - __u64 vblocknr; /* virtual block number */ - unsigned long blkoff; /* File offset of the data block (per block) */ - struct list_head list; -}; - - -static int nilfs_warn_segment_error(int err) -{ - switch (err) { - case NILFS_SEG_FAIL_IO: - printk(KERN_WARNING - "NILFS warning: I/O error on loading last segment\n"); - return -EIO; - case NILFS_SEG_FAIL_MAGIC: - printk(KERN_WARNING - "NILFS warning: Segment magic number invalid\n"); - break; - case NILFS_SEG_FAIL_SEQ: - printk(KERN_WARNING - "NILFS warning: Sequence number mismatch\n"); - break; - case NILFS_SEG_FAIL_CHECKSUM_SUPER_ROOT: - printk(KERN_WARNING - "NILFS warning: Checksum error in super root\n"); - break; - case NILFS_SEG_FAIL_CHECKSUM_FULL: - printk(KERN_WARNING - "NILFS warning: Checksum error in segment payload\n"); - break; - case NILFS_SEG_FAIL_CONSISTENCY: - printk(KERN_WARNING - "NILFS warning: Inconsistent segment\n"); - break; - case NILFS_SEG_NO_SUPER_ROOT: - printk(KERN_WARNING - "NILFS warning: No super root in the last segment\n"); - break; - } - return -EINVAL; -} - -/** - * nilfs_compute_checksum - compute checksum of blocks continuously - * @nilfs: nilfs object - * @bhs: buffer head of start block - * @sum: place to store result - * @offset: offset bytes in the first block - * @check_bytes: number of bytes to be checked - * @start: DBN of start block - * @nblock: number of blocks to be checked - */ -static int nilfs_compute_checksum(struct the_nilfs *nilfs, - struct buffer_head *bhs, u32 *sum, - unsigned long offset, u64 check_bytes, - sector_t start, unsigned long nblock) -{ - unsigned int blocksize = nilfs->ns_blocksize; - unsigned long size; - u32 crc; - - BUG_ON(offset >= blocksize); - check_bytes -= offset; - size = min_t(u64, check_bytes, blocksize - offset); - crc = crc32_le(nilfs->ns_crc_seed, - (unsigned char *)bhs->b_data + offset, size); - if (--nblock > 0) { - do { - struct buffer_head *bh; - - bh = __bread(nilfs->ns_bdev, ++start, blocksize); - if (!bh) - return -EIO; - check_bytes -= size; - size = min_t(u64, check_bytes, blocksize); - crc = crc32_le(crc, bh->b_data, size); - brelse(bh); - } while (--nblock > 0); - } - *sum = crc; - return 0; -} - -/** - * nilfs_read_super_root_block - read super root block - * @nilfs: nilfs object - * @sr_block: disk block number of the super root block - * @pbh: address of a buffer_head pointer to return super root buffer - * @check: CRC check flag - */ -int nilfs_read_super_root_block(struct the_nilfs *nilfs, sector_t sr_block, - struct buffer_head **pbh, int check) -{ - struct buffer_head *bh_sr; - struct nilfs_super_root *sr; - u32 crc; - int ret; - - *pbh = NULL; - bh_sr = __bread(nilfs->ns_bdev, sr_block, nilfs->ns_blocksize); - if (unlikely(!bh_sr)) { - ret = NILFS_SEG_FAIL_IO; - goto failed; - } - - sr = (struct nilfs_super_root *)bh_sr->b_data; - if (check) { - unsigned bytes = le16_to_cpu(sr->sr_bytes); - - if (bytes == 0 || bytes > nilfs->ns_blocksize) { - ret = NILFS_SEG_FAIL_CHECKSUM_SUPER_ROOT; - goto failed_bh; - } - if (nilfs_compute_checksum( - nilfs, bh_sr, &crc, sizeof(sr->sr_sum), bytes, - sr_block, 1)) { - ret = NILFS_SEG_FAIL_IO; - goto failed_bh; - } - if (crc != le32_to_cpu(sr->sr_sum)) { - ret = NILFS_SEG_FAIL_CHECKSUM_SUPER_ROOT; - goto failed_bh; - } - } - *pbh = bh_sr; - return 0; - - failed_bh: - brelse(bh_sr); - - failed: - return nilfs_warn_segment_error(ret); -} - -/** - * nilfs_read_log_header - read summary header of the specified log - * @nilfs: nilfs object - * @start_blocknr: start block number of the log - * @sum: pointer to return segment summary structure - */ -static struct buffer_head * -nilfs_read_log_header(struct the_nilfs *nilfs, sector_t start_blocknr, - struct nilfs_segment_summary **sum) -{ - struct buffer_head *bh_sum; - - bh_sum = __bread(nilfs->ns_bdev, start_blocknr, nilfs->ns_blocksize); - if (bh_sum) - *sum = (struct nilfs_segment_summary *)bh_sum->b_data; - return bh_sum; -} - -/** - * nilfs_validate_log - verify consistency of log - * @nilfs: nilfs object - * @seg_seq: sequence number of segment - * @bh_sum: buffer head of summary block - * @sum: segment summary struct - */ -static int nilfs_validate_log(struct the_nilfs *nilfs, u64 seg_seq, - struct buffer_head *bh_sum, - struct nilfs_segment_summary *sum) -{ - unsigned long nblock; - u32 crc; - int ret; - - ret = NILFS_SEG_FAIL_MAGIC; - if (le32_to_cpu(sum->ss_magic) != NILFS_SEGSUM_MAGIC) - goto out; - - ret = NILFS_SEG_FAIL_SEQ; - if (le64_to_cpu(sum->ss_seq) != seg_seq) - goto out; - - nblock = le32_to_cpu(sum->ss_nblocks); - ret = NILFS_SEG_FAIL_CONSISTENCY; - if (unlikely(nblock == 0 || nblock > nilfs->ns_blocks_per_segment)) - /* This limits the number of blocks read in the CRC check */ - goto out; - - ret = NILFS_SEG_FAIL_IO; - if (nilfs_compute_checksum(nilfs, bh_sum, &crc, sizeof(sum->ss_datasum), - ((u64)nblock << nilfs->ns_blocksize_bits), - bh_sum->b_blocknr, nblock)) - goto out; - - ret = NILFS_SEG_FAIL_CHECKSUM_FULL; - if (crc != le32_to_cpu(sum->ss_datasum)) - goto out; - ret = 0; -out: - return ret; -} - -/** - * nilfs_read_summary_info - read an item on summary blocks of a log - * @nilfs: nilfs object - * @pbh: the current buffer head on summary blocks [in, out] - * @offset: the current byte offset on summary blocks [in, out] - * @bytes: byte size of the item to be read - */ -static void *nilfs_read_summary_info(struct the_nilfs *nilfs, - struct buffer_head **pbh, - unsigned int *offset, unsigned int bytes) -{ - void *ptr; - sector_t blocknr; - - BUG_ON((*pbh)->b_size < *offset); - if (bytes > (*pbh)->b_size - *offset) { - blocknr = (*pbh)->b_blocknr; - brelse(*pbh); - *pbh = __bread(nilfs->ns_bdev, blocknr + 1, - nilfs->ns_blocksize); - if (unlikely(!*pbh)) - return NULL; - *offset = 0; - } - ptr = (*pbh)->b_data + *offset; - *offset += bytes; - return ptr; -} - -/** - * nilfs_skip_summary_info - skip items on summary blocks of a log - * @nilfs: nilfs object - * @pbh: the current buffer head on summary blocks [in, out] - * @offset: the current byte offset on summary blocks [in, out] - * @bytes: byte size of the item to be skipped - * @count: number of items to be skipped - */ -static void nilfs_skip_summary_info(struct the_nilfs *nilfs, - struct buffer_head **pbh, - unsigned int *offset, unsigned int bytes, - unsigned long count) -{ - unsigned int rest_item_in_current_block - = ((*pbh)->b_size - *offset) / bytes; - - if (count <= rest_item_in_current_block) { - *offset += bytes * count; - } else { - sector_t blocknr = (*pbh)->b_blocknr; - unsigned int nitem_per_block = (*pbh)->b_size / bytes; - unsigned int bcnt; - - count -= rest_item_in_current_block; - bcnt = DIV_ROUND_UP(count, nitem_per_block); - *offset = bytes * (count - (bcnt - 1) * nitem_per_block); - - brelse(*pbh); - *pbh = __bread(nilfs->ns_bdev, blocknr + bcnt, - nilfs->ns_blocksize); - } -} - -/** - * nilfs_scan_dsync_log - get block information of a log written for data sync - * @nilfs: nilfs object - * @start_blocknr: start block number of the log - * @sum: log summary information - * @head: list head to add nilfs_recovery_block struct - */ -static int nilfs_scan_dsync_log(struct the_nilfs *nilfs, sector_t start_blocknr, - struct nilfs_segment_summary *sum, - struct list_head *head) -{ - struct buffer_head *bh; - unsigned int offset; - u32 nfinfo, sumbytes; - sector_t blocknr; - ino_t ino; - int err = -EIO; - - nfinfo = le32_to_cpu(sum->ss_nfinfo); - if (!nfinfo) - return 0; - - sumbytes = le32_to_cpu(sum->ss_sumbytes); - blocknr = start_blocknr + DIV_ROUND_UP(sumbytes, nilfs->ns_blocksize); - bh = __bread(nilfs->ns_bdev, start_blocknr, nilfs->ns_blocksize); - if (unlikely(!bh)) - goto out; - - offset = le16_to_cpu(sum->ss_bytes); - for (;;) { - unsigned long nblocks, ndatablk, nnodeblk; - struct nilfs_finfo *finfo; - - finfo = nilfs_read_summary_info(nilfs, &bh, &offset, - sizeof(*finfo)); - if (unlikely(!finfo)) - goto out; - - ino = le64_to_cpu(finfo->fi_ino); - nblocks = le32_to_cpu(finfo->fi_nblocks); - ndatablk = le32_to_cpu(finfo->fi_ndatablk); - nnodeblk = nblocks - ndatablk; - - while (ndatablk-- > 0) { - struct nilfs_recovery_block *rb; - struct nilfs_binfo_v *binfo; - - binfo = nilfs_read_summary_info(nilfs, &bh, &offset, - sizeof(*binfo)); - if (unlikely(!binfo)) - goto out; - - rb = kmalloc(sizeof(*rb), GFP_NOFS); - if (unlikely(!rb)) { - err = -ENOMEM; - goto out; - } - rb->ino = ino; - rb->blocknr = blocknr++; - rb->vblocknr = le64_to_cpu(binfo->bi_vblocknr); - rb->blkoff = le64_to_cpu(binfo->bi_blkoff); - /* INIT_LIST_HEAD(&rb->list); */ - list_add_tail(&rb->list, head); - } - if (--nfinfo == 0) - break; - blocknr += nnodeblk; /* always 0 for data sync logs */ - nilfs_skip_summary_info(nilfs, &bh, &offset, sizeof(__le64), - nnodeblk); - if (unlikely(!bh)) - goto out; - } - err = 0; - out: - brelse(bh); /* brelse(NULL) is just ignored */ - return err; -} - -static void dispose_recovery_list(struct list_head *head) -{ - while (!list_empty(head)) { - struct nilfs_recovery_block *rb; - - rb = list_first_entry(head, struct nilfs_recovery_block, list); - list_del(&rb->list); - kfree(rb); - } -} - -struct nilfs_segment_entry { - struct list_head list; - __u64 segnum; -}; - -static int nilfs_segment_list_add(struct list_head *head, __u64 segnum) -{ - struct nilfs_segment_entry *ent = kmalloc(sizeof(*ent), GFP_NOFS); - - if (unlikely(!ent)) - return -ENOMEM; - - ent->segnum = segnum; - INIT_LIST_HEAD(&ent->list); - list_add_tail(&ent->list, head); - return 0; -} - -void nilfs_dispose_segment_list(struct list_head *head) -{ - while (!list_empty(head)) { - struct nilfs_segment_entry *ent; - - ent = list_first_entry(head, struct nilfs_segment_entry, list); - list_del(&ent->list); - kfree(ent); - } -} - -static int nilfs_prepare_segment_for_recovery(struct the_nilfs *nilfs, - struct super_block *sb, - struct nilfs_recovery_info *ri) -{ - struct list_head *head = &ri->ri_used_segments; - struct nilfs_segment_entry *ent, *n; - struct inode *sufile = nilfs->ns_sufile; - __u64 segnum[4]; - int err; - int i; - - segnum[0] = nilfs->ns_segnum; - segnum[1] = nilfs->ns_nextnum; - segnum[2] = ri->ri_segnum; - segnum[3] = ri->ri_nextnum; - - /* - * Releasing the next segment of the latest super root. - * The next segment is invalidated by this recovery. - */ - err = nilfs_sufile_free(sufile, segnum[1]); - if (unlikely(err)) - goto failed; - - for (i = 1; i < 4; i++) { - err = nilfs_segment_list_add(head, segnum[i]); - if (unlikely(err)) - goto failed; - } - - /* - * Collecting segments written after the latest super root. - * These are marked dirty to avoid being reallocated in the next write. - */ - list_for_each_entry_safe(ent, n, head, list) { - if (ent->segnum != segnum[0]) { - err = nilfs_sufile_scrap(sufile, ent->segnum); - if (unlikely(err)) - goto failed; - } - list_del(&ent->list); - kfree(ent); - } - - /* Allocate new segments for recovery */ - err = nilfs_sufile_alloc(sufile, &segnum[0]); - if (unlikely(err)) - goto failed; - - nilfs->ns_pseg_offset = 0; - nilfs->ns_seg_seq = ri->ri_seq + 2; - nilfs->ns_nextnum = nilfs->ns_segnum = segnum[0]; - - failed: - /* No need to recover sufile because it will be destroyed on error */ - return err; -} - -static int nilfs_recovery_copy_block(struct the_nilfs *nilfs, - struct nilfs_recovery_block *rb, - struct page *page) -{ - struct buffer_head *bh_org; - void *kaddr; - - bh_org = __bread(nilfs->ns_bdev, rb->blocknr, nilfs->ns_blocksize); - if (unlikely(!bh_org)) - return -EIO; - - kaddr = kmap_atomic(page); - memcpy(kaddr + bh_offset(bh_org), bh_org->b_data, bh_org->b_size); - kunmap_atomic(kaddr); - brelse(bh_org); - return 0; -} - -static int nilfs_recover_dsync_blocks(struct the_nilfs *nilfs, - struct super_block *sb, - struct nilfs_root *root, - struct list_head *head, - unsigned long *nr_salvaged_blocks) -{ - struct inode *inode; - struct nilfs_recovery_block *rb, *n; - unsigned blocksize = nilfs->ns_blocksize; - struct page *page; - loff_t pos; - int err = 0, err2 = 0; - - list_for_each_entry_safe(rb, n, head, list) { - inode = nilfs_iget(sb, root, rb->ino); - if (IS_ERR(inode)) { - err = PTR_ERR(inode); - inode = NULL; - goto failed_inode; - } - - pos = rb->blkoff << inode->i_blkbits; - err = block_write_begin(inode->i_mapping, pos, blocksize, - 0, &page, nilfs_get_block); - if (unlikely(err)) { - loff_t isize = inode->i_size; - if (pos + blocksize > isize) - vmtruncate(inode, isize); - goto failed_inode; - } - - err = nilfs_recovery_copy_block(nilfs, rb, page); - if (unlikely(err)) - goto failed_page; - - err = nilfs_set_file_dirty(inode, 1); - if (unlikely(err)) - goto failed_page; - - block_write_end(NULL, inode->i_mapping, pos, blocksize, - blocksize, page, NULL); - - unlock_page(page); - page_cache_release(page); - - (*nr_salvaged_blocks)++; - goto next; - - failed_page: - unlock_page(page); - page_cache_release(page); - - failed_inode: - printk(KERN_WARNING - "NILFS warning: error recovering data block " - "(err=%d, ino=%lu, block-offset=%llu)\n", - err, (unsigned long)rb->ino, - (unsigned long long)rb->blkoff); - if (!err2) - err2 = err; - next: - iput(inode); /* iput(NULL) is just ignored */ - list_del_init(&rb->list); - kfree(rb); - } - return err2; -} - -/** - * nilfs_do_roll_forward - salvage logical segments newer than the latest - * checkpoint - * @nilfs: nilfs object - * @sb: super block instance - * @ri: pointer to a nilfs_recovery_info - */ -static int nilfs_do_roll_forward(struct the_nilfs *nilfs, - struct super_block *sb, - struct nilfs_root *root, - struct nilfs_recovery_info *ri) -{ - struct buffer_head *bh_sum = NULL; - struct nilfs_segment_summary *sum; - sector_t pseg_start; - sector_t seg_start, seg_end; /* Starting/ending DBN of full segment */ - unsigned long nsalvaged_blocks = 0; - unsigned int flags; - u64 seg_seq; - __u64 segnum, nextnum = 0; - int empty_seg = 0; - int err = 0, ret; - LIST_HEAD(dsync_blocks); /* list of data blocks to be recovered */ - enum { - RF_INIT_ST, - RF_DSYNC_ST, /* scanning data-sync segments */ - }; - int state = RF_INIT_ST; - - pseg_start = ri->ri_lsegs_start; - seg_seq = ri->ri_lsegs_start_seq; - segnum = nilfs_get_segnum_of_block(nilfs, pseg_start); - nilfs_get_segment_range(nilfs, segnum, &seg_start, &seg_end); - - while (segnum != ri->ri_segnum || pseg_start <= ri->ri_pseg_start) { - brelse(bh_sum); - bh_sum = nilfs_read_log_header(nilfs, pseg_start, &sum); - if (!bh_sum) { - err = -EIO; - goto failed; - } - - ret = nilfs_validate_log(nilfs, seg_seq, bh_sum, sum); - if (ret) { - if (ret == NILFS_SEG_FAIL_IO) { - err = -EIO; - goto failed; - } - goto strayed; - } - - flags = le16_to_cpu(sum->ss_flags); - if (flags & NILFS_SS_SR) - goto confused; - - /* Found a valid partial segment; do recovery actions */ - nextnum = nilfs_get_segnum_of_block(nilfs, - le64_to_cpu(sum->ss_next)); - empty_seg = 0; - nilfs->ns_ctime = le64_to_cpu(sum->ss_create); - if (!(flags & NILFS_SS_GC)) - nilfs->ns_nongc_ctime = nilfs->ns_ctime; - - switch (state) { - case RF_INIT_ST: - if (!(flags & NILFS_SS_LOGBGN) || - !(flags & NILFS_SS_SYNDT)) - goto try_next_pseg; - state = RF_DSYNC_ST; - /* Fall through */ - case RF_DSYNC_ST: - if (!(flags & NILFS_SS_SYNDT)) - goto confused; - - err = nilfs_scan_dsync_log(nilfs, pseg_start, sum, - &dsync_blocks); - if (unlikely(err)) - goto failed; - if (flags & NILFS_SS_LOGEND) { - err = nilfs_recover_dsync_blocks( - nilfs, sb, root, &dsync_blocks, - &nsalvaged_blocks); - if (unlikely(err)) - goto failed; - state = RF_INIT_ST; - } - break; /* Fall through to try_next_pseg */ - } - - try_next_pseg: - if (pseg_start == ri->ri_lsegs_end) - break; - pseg_start += le32_to_cpu(sum->ss_nblocks); - if (pseg_start < seg_end) - continue; - goto feed_segment; - - strayed: - if (pseg_start == ri->ri_lsegs_end) - break; - - feed_segment: - /* Looking to the next full segment */ - if (empty_seg++) - break; - seg_seq++; - segnum = nextnum; - nilfs_get_segment_range(nilfs, segnum, &seg_start, &seg_end); - pseg_start = seg_start; - } - - if (nsalvaged_blocks) { - printk(KERN_INFO "NILFS (device %s): salvaged %lu blocks\n", - sb->s_id, nsalvaged_blocks); - ri->ri_need_recovery = NILFS_RECOVERY_ROLLFORWARD_DONE; - } - out: - brelse(bh_sum); - dispose_recovery_list(&dsync_blocks); - return err; - - confused: - err = -EINVAL; - failed: - printk(KERN_ERR - "NILFS (device %s): Error roll-forwarding " - "(err=%d, pseg block=%llu). ", - sb->s_id, err, (unsigned long long)pseg_start); - goto out; -} - -static void nilfs_finish_roll_forward(struct the_nilfs *nilfs, - struct nilfs_recovery_info *ri) -{ - struct buffer_head *bh; - int err; - - if (nilfs_get_segnum_of_block(nilfs, ri->ri_lsegs_start) != - nilfs_get_segnum_of_block(nilfs, ri->ri_super_root)) - return; - - bh = __getblk(nilfs->ns_bdev, ri->ri_lsegs_start, nilfs->ns_blocksize); - BUG_ON(!bh); - memset(bh->b_data, 0, bh->b_size); - set_buffer_dirty(bh); - err = sync_dirty_buffer(bh); - if (unlikely(err)) - printk(KERN_WARNING - "NILFS warning: buffer sync write failed during " - "post-cleaning of recovery.\n"); - brelse(bh); -} - -/** - * nilfs_salvage_orphan_logs - salvage logs written after the latest checkpoint - * @nilfs: nilfs object - * @sb: super block instance - * @ri: pointer to a nilfs_recovery_info struct to store search results. - * - * Return Value: On success, 0 is returned. On error, one of the following - * negative error code is returned. - * - * %-EINVAL - Inconsistent filesystem state. - * - * %-EIO - I/O error - * - * %-ENOSPC - No space left on device (only in a panic state). - * - * %-ERESTARTSYS - Interrupted. - * - * %-ENOMEM - Insufficient memory available. - */ -int nilfs_salvage_orphan_logs(struct the_nilfs *nilfs, - struct super_block *sb, - struct nilfs_recovery_info *ri) -{ - struct nilfs_root *root; - int err; - - if (ri->ri_lsegs_start == 0 || ri->ri_lsegs_end == 0) - return 0; - - err = nilfs_attach_checkpoint(sb, ri->ri_cno, true, &root); - if (unlikely(err)) { - printk(KERN_ERR - "NILFS: error loading the latest checkpoint.\n"); - return err; - } - - err = nilfs_do_roll_forward(nilfs, sb, root, ri); - if (unlikely(err)) - goto failed; - - if (ri->ri_need_recovery == NILFS_RECOVERY_ROLLFORWARD_DONE) { - err = nilfs_prepare_segment_for_recovery(nilfs, sb, ri); - if (unlikely(err)) { - printk(KERN_ERR "NILFS: Error preparing segments for " - "recovery.\n"); - goto failed; - } - - err = nilfs_attach_log_writer(sb, root); - if (unlikely(err)) - goto failed; - - set_nilfs_discontinued(nilfs); - err = nilfs_construct_segment(sb); - nilfs_detach_log_writer(sb); - - if (unlikely(err)) { - printk(KERN_ERR "NILFS: Oops! recovery failed. " - "(err=%d)\n", err); - goto failed; - } - - nilfs_finish_roll_forward(nilfs, ri); - } - - failed: - nilfs_put_root(root); - return err; -} - -/** - * nilfs_search_super_root - search the latest valid super root - * @nilfs: the_nilfs - * @ri: pointer to a nilfs_recovery_info struct to store search results. - * - * nilfs_search_super_root() looks for the latest super-root from a partial - * segment pointed by the superblock. It sets up struct the_nilfs through - * this search. It fills nilfs_recovery_info (ri) required for recovery. - * - * Return Value: On success, 0 is returned. On error, one of the following - * negative error code is returned. - * - * %-EINVAL - No valid segment found - * - * %-EIO - I/O error - * - * %-ENOMEM - Insufficient memory available. - */ -int nilfs_search_super_root(struct the_nilfs *nilfs, - struct nilfs_recovery_info *ri) -{ - struct buffer_head *bh_sum = NULL; - struct nilfs_segment_summary *sum; - sector_t pseg_start, pseg_end, sr_pseg_start = 0; - sector_t seg_start, seg_end; /* range of full segment (block number) */ - sector_t b, end; - unsigned long nblocks; - unsigned int flags; - u64 seg_seq; - __u64 segnum, nextnum = 0; - __u64 cno; - LIST_HEAD(segments); - int empty_seg = 0, scan_newer = 0; - int ret; - - pseg_start = nilfs->ns_last_pseg; - seg_seq = nilfs->ns_last_seq; - cno = nilfs->ns_last_cno; - segnum = nilfs_get_segnum_of_block(nilfs, pseg_start); - - /* Calculate range of segment */ - nilfs_get_segment_range(nilfs, segnum, &seg_start, &seg_end); - - /* Read ahead segment */ - b = seg_start; - while (b <= seg_end) - __breadahead(nilfs->ns_bdev, b++, nilfs->ns_blocksize); - - for (;;) { - brelse(bh_sum); - ret = NILFS_SEG_FAIL_IO; - bh_sum = nilfs_read_log_header(nilfs, pseg_start, &sum); - if (!bh_sum) - goto failed; - - ret = nilfs_validate_log(nilfs, seg_seq, bh_sum, sum); - if (ret) { - if (ret == NILFS_SEG_FAIL_IO) - goto failed; - goto strayed; - } - - nblocks = le32_to_cpu(sum->ss_nblocks); - pseg_end = pseg_start + nblocks - 1; - if (unlikely(pseg_end > seg_end)) { - ret = NILFS_SEG_FAIL_CONSISTENCY; - goto strayed; - } - - /* A valid partial segment */ - ri->ri_pseg_start = pseg_start; - ri->ri_seq = seg_seq; - ri->ri_segnum = segnum; - nextnum = nilfs_get_segnum_of_block(nilfs, - le64_to_cpu(sum->ss_next)); - ri->ri_nextnum = nextnum; - empty_seg = 0; - - flags = le16_to_cpu(sum->ss_flags); - if (!(flags & NILFS_SS_SR) && !scan_newer) { - /* This will never happen because a superblock - (last_segment) always points to a pseg - having a super root. */ - ret = NILFS_SEG_FAIL_CONSISTENCY; - goto failed; - } - - if (pseg_start == seg_start) { - nilfs_get_segment_range(nilfs, nextnum, &b, &end); - while (b <= end) - __breadahead(nilfs->ns_bdev, b++, - nilfs->ns_blocksize); - } - if (!(flags & NILFS_SS_SR)) { - if (!ri->ri_lsegs_start && (flags & NILFS_SS_LOGBGN)) { - ri->ri_lsegs_start = pseg_start; - ri->ri_lsegs_start_seq = seg_seq; - } - if (flags & NILFS_SS_LOGEND) - ri->ri_lsegs_end = pseg_start; - goto try_next_pseg; - } - - /* A valid super root was found. */ - ri->ri_cno = cno++; - ri->ri_super_root = pseg_end; - ri->ri_lsegs_start = ri->ri_lsegs_end = 0; - - nilfs_dispose_segment_list(&segments); - sr_pseg_start = pseg_start; - nilfs->ns_pseg_offset = pseg_start + nblocks - seg_start; - nilfs->ns_seg_seq = seg_seq; - nilfs->ns_segnum = segnum; - nilfs->ns_cno = cno; /* nilfs->ns_cno = ri->ri_cno + 1 */ - nilfs->ns_ctime = le64_to_cpu(sum->ss_create); - nilfs->ns_nextnum = nextnum; - - if (scan_newer) - ri->ri_need_recovery = NILFS_RECOVERY_SR_UPDATED; - else { - if (nilfs->ns_mount_state & NILFS_VALID_FS) - goto super_root_found; - scan_newer = 1; - } - - try_next_pseg: - /* Standing on a course, or met an inconsistent state */ - pseg_start += nblocks; - if (pseg_start < seg_end) - continue; - goto feed_segment; - - strayed: - /* Off the trail */ - if (!scan_newer) - /* - * This can happen if a checkpoint was written without - * barriers, or as a result of an I/O failure. - */ - goto failed; - - feed_segment: - /* Looking to the next full segment */ - if (empty_seg++) - goto super_root_found; /* found a valid super root */ - - ret = nilfs_segment_list_add(&segments, segnum); - if (unlikely(ret)) - goto failed; - - seg_seq++; - segnum = nextnum; - nilfs_get_segment_range(nilfs, segnum, &seg_start, &seg_end); - pseg_start = seg_start; - } - - super_root_found: - /* Updating pointers relating to the latest checkpoint */ - brelse(bh_sum); - list_splice_tail(&segments, &ri->ri_used_segments); - nilfs->ns_last_pseg = sr_pseg_start; - nilfs->ns_last_seq = nilfs->ns_seg_seq; - nilfs->ns_last_cno = ri->ri_cno; - return 0; - - failed: - brelse(bh_sum); - nilfs_dispose_segment_list(&segments); - return (ret < 0) ? ret : nilfs_warn_segment_error(ret); -} diff --git a/ANDROID_3.4.5/fs/nilfs2/segbuf.c b/ANDROID_3.4.5/fs/nilfs2/segbuf.c deleted file mode 100644 index dc9a9137..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/segbuf.c +++ /dev/null @@ -1,536 +0,0 @@ -/* - * segbuf.c - NILFS segment buffer - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Ryusuke Konishi <ryusuke@osrg.net> - * - */ - -#include <linux/buffer_head.h> -#include <linux/writeback.h> -#include <linux/crc32.h> -#include <linux/backing-dev.h> -#include <linux/slab.h> -#include "page.h" -#include "segbuf.h" - - -struct nilfs_write_info { - struct the_nilfs *nilfs; - struct bio *bio; - int start, end; /* The region to be submitted */ - int rest_blocks; - int max_pages; - int nr_vecs; - sector_t blocknr; -}; - -static int nilfs_segbuf_write(struct nilfs_segment_buffer *segbuf, - struct the_nilfs *nilfs); -static int nilfs_segbuf_wait(struct nilfs_segment_buffer *segbuf); - -struct nilfs_segment_buffer *nilfs_segbuf_new(struct super_block *sb) -{ - struct nilfs_segment_buffer *segbuf; - - segbuf = kmem_cache_alloc(nilfs_segbuf_cachep, GFP_NOFS); - if (unlikely(!segbuf)) - return NULL; - - segbuf->sb_super = sb; - INIT_LIST_HEAD(&segbuf->sb_list); - INIT_LIST_HEAD(&segbuf->sb_segsum_buffers); - INIT_LIST_HEAD(&segbuf->sb_payload_buffers); - segbuf->sb_super_root = NULL; - - init_completion(&segbuf->sb_bio_event); - atomic_set(&segbuf->sb_err, 0); - segbuf->sb_nbio = 0; - - return segbuf; -} - -void nilfs_segbuf_free(struct nilfs_segment_buffer *segbuf) -{ - kmem_cache_free(nilfs_segbuf_cachep, segbuf); -} - -void nilfs_segbuf_map(struct nilfs_segment_buffer *segbuf, __u64 segnum, - unsigned long offset, struct the_nilfs *nilfs) -{ - segbuf->sb_segnum = segnum; - nilfs_get_segment_range(nilfs, segnum, &segbuf->sb_fseg_start, - &segbuf->sb_fseg_end); - - segbuf->sb_pseg_start = segbuf->sb_fseg_start + offset; - segbuf->sb_rest_blocks = - segbuf->sb_fseg_end - segbuf->sb_pseg_start + 1; -} - -/** - * nilfs_segbuf_map_cont - map a new log behind a given log - * @segbuf: new segment buffer - * @prev: segment buffer containing a log to be continued - */ -void nilfs_segbuf_map_cont(struct nilfs_segment_buffer *segbuf, - struct nilfs_segment_buffer *prev) -{ - segbuf->sb_segnum = prev->sb_segnum; - segbuf->sb_fseg_start = prev->sb_fseg_start; - segbuf->sb_fseg_end = prev->sb_fseg_end; - segbuf->sb_pseg_start = prev->sb_pseg_start + prev->sb_sum.nblocks; - segbuf->sb_rest_blocks = - segbuf->sb_fseg_end - segbuf->sb_pseg_start + 1; -} - -void nilfs_segbuf_set_next_segnum(struct nilfs_segment_buffer *segbuf, - __u64 nextnum, struct the_nilfs *nilfs) -{ - segbuf->sb_nextnum = nextnum; - segbuf->sb_sum.next = nilfs_get_segment_start_blocknr(nilfs, nextnum); -} - -int nilfs_segbuf_extend_segsum(struct nilfs_segment_buffer *segbuf) -{ - struct buffer_head *bh; - - bh = sb_getblk(segbuf->sb_super, - segbuf->sb_pseg_start + segbuf->sb_sum.nsumblk); - if (unlikely(!bh)) - return -ENOMEM; - - nilfs_segbuf_add_segsum_buffer(segbuf, bh); - return 0; -} - -int nilfs_segbuf_extend_payload(struct nilfs_segment_buffer *segbuf, - struct buffer_head **bhp) -{ - struct buffer_head *bh; - - bh = sb_getblk(segbuf->sb_super, - segbuf->sb_pseg_start + segbuf->sb_sum.nblocks); - if (unlikely(!bh)) - return -ENOMEM; - - nilfs_segbuf_add_payload_buffer(segbuf, bh); - *bhp = bh; - return 0; -} - -int nilfs_segbuf_reset(struct nilfs_segment_buffer *segbuf, unsigned flags, - time_t ctime, __u64 cno) -{ - int err; - - segbuf->sb_sum.nblocks = segbuf->sb_sum.nsumblk = 0; - err = nilfs_segbuf_extend_segsum(segbuf); - if (unlikely(err)) - return err; - - segbuf->sb_sum.flags = flags; - segbuf->sb_sum.sumbytes = sizeof(struct nilfs_segment_summary); - segbuf->sb_sum.nfinfo = segbuf->sb_sum.nfileblk = 0; - segbuf->sb_sum.ctime = ctime; - segbuf->sb_sum.cno = cno; - return 0; -} - -/* - * Setup segment summary - */ -void nilfs_segbuf_fill_in_segsum(struct nilfs_segment_buffer *segbuf) -{ - struct nilfs_segment_summary *raw_sum; - struct buffer_head *bh_sum; - - bh_sum = list_entry(segbuf->sb_segsum_buffers.next, - struct buffer_head, b_assoc_buffers); - raw_sum = (struct nilfs_segment_summary *)bh_sum->b_data; - - raw_sum->ss_magic = cpu_to_le32(NILFS_SEGSUM_MAGIC); - raw_sum->ss_bytes = cpu_to_le16(sizeof(*raw_sum)); - raw_sum->ss_flags = cpu_to_le16(segbuf->sb_sum.flags); - raw_sum->ss_seq = cpu_to_le64(segbuf->sb_sum.seg_seq); - raw_sum->ss_create = cpu_to_le64(segbuf->sb_sum.ctime); - raw_sum->ss_next = cpu_to_le64(segbuf->sb_sum.next); - raw_sum->ss_nblocks = cpu_to_le32(segbuf->sb_sum.nblocks); - raw_sum->ss_nfinfo = cpu_to_le32(segbuf->sb_sum.nfinfo); - raw_sum->ss_sumbytes = cpu_to_le32(segbuf->sb_sum.sumbytes); - raw_sum->ss_pad = 0; - raw_sum->ss_cno = cpu_to_le64(segbuf->sb_sum.cno); -} - -/* - * CRC calculation routines - */ -static void -nilfs_segbuf_fill_in_segsum_crc(struct nilfs_segment_buffer *segbuf, u32 seed) -{ - struct buffer_head *bh; - struct nilfs_segment_summary *raw_sum; - unsigned long size, bytes = segbuf->sb_sum.sumbytes; - u32 crc; - - bh = list_entry(segbuf->sb_segsum_buffers.next, struct buffer_head, - b_assoc_buffers); - - raw_sum = (struct nilfs_segment_summary *)bh->b_data; - size = min_t(unsigned long, bytes, bh->b_size); - crc = crc32_le(seed, - (unsigned char *)raw_sum + - sizeof(raw_sum->ss_datasum) + sizeof(raw_sum->ss_sumsum), - size - (sizeof(raw_sum->ss_datasum) + - sizeof(raw_sum->ss_sumsum))); - - list_for_each_entry_continue(bh, &segbuf->sb_segsum_buffers, - b_assoc_buffers) { - bytes -= size; - size = min_t(unsigned long, bytes, bh->b_size); - crc = crc32_le(crc, bh->b_data, size); - } - raw_sum->ss_sumsum = cpu_to_le32(crc); -} - -static void nilfs_segbuf_fill_in_data_crc(struct nilfs_segment_buffer *segbuf, - u32 seed) -{ - struct buffer_head *bh; - struct nilfs_segment_summary *raw_sum; - void *kaddr; - u32 crc; - - bh = list_entry(segbuf->sb_segsum_buffers.next, struct buffer_head, - b_assoc_buffers); - raw_sum = (struct nilfs_segment_summary *)bh->b_data; - crc = crc32_le(seed, - (unsigned char *)raw_sum + sizeof(raw_sum->ss_datasum), - bh->b_size - sizeof(raw_sum->ss_datasum)); - - list_for_each_entry_continue(bh, &segbuf->sb_segsum_buffers, - b_assoc_buffers) { - crc = crc32_le(crc, bh->b_data, bh->b_size); - } - list_for_each_entry(bh, &segbuf->sb_payload_buffers, b_assoc_buffers) { - kaddr = kmap_atomic(bh->b_page); - crc = crc32_le(crc, kaddr + bh_offset(bh), bh->b_size); - kunmap_atomic(kaddr); - } - raw_sum->ss_datasum = cpu_to_le32(crc); -} - -static void -nilfs_segbuf_fill_in_super_root_crc(struct nilfs_segment_buffer *segbuf, - u32 seed) -{ - struct nilfs_super_root *raw_sr; - struct the_nilfs *nilfs = segbuf->sb_super->s_fs_info; - unsigned srsize; - u32 crc; - - raw_sr = (struct nilfs_super_root *)segbuf->sb_super_root->b_data; - srsize = NILFS_SR_BYTES(nilfs->ns_inode_size); - crc = crc32_le(seed, - (unsigned char *)raw_sr + sizeof(raw_sr->sr_sum), - srsize - sizeof(raw_sr->sr_sum)); - raw_sr->sr_sum = cpu_to_le32(crc); -} - -static void nilfs_release_buffers(struct list_head *list) -{ - struct buffer_head *bh, *n; - - list_for_each_entry_safe(bh, n, list, b_assoc_buffers) { - list_del_init(&bh->b_assoc_buffers); - brelse(bh); - } -} - -static void nilfs_segbuf_clear(struct nilfs_segment_buffer *segbuf) -{ - nilfs_release_buffers(&segbuf->sb_segsum_buffers); - nilfs_release_buffers(&segbuf->sb_payload_buffers); - segbuf->sb_super_root = NULL; -} - -/* - * Iterators for segment buffers - */ -void nilfs_clear_logs(struct list_head *logs) -{ - struct nilfs_segment_buffer *segbuf; - - list_for_each_entry(segbuf, logs, sb_list) - nilfs_segbuf_clear(segbuf); -} - -void nilfs_truncate_logs(struct list_head *logs, - struct nilfs_segment_buffer *last) -{ - struct nilfs_segment_buffer *n, *segbuf; - - segbuf = list_prepare_entry(last, logs, sb_list); - list_for_each_entry_safe_continue(segbuf, n, logs, sb_list) { - list_del_init(&segbuf->sb_list); - nilfs_segbuf_clear(segbuf); - nilfs_segbuf_free(segbuf); - } -} - -int nilfs_write_logs(struct list_head *logs, struct the_nilfs *nilfs) -{ - struct nilfs_segment_buffer *segbuf; - int ret = 0; - - list_for_each_entry(segbuf, logs, sb_list) { - ret = nilfs_segbuf_write(segbuf, nilfs); - if (ret) - break; - } - return ret; -} - -int nilfs_wait_on_logs(struct list_head *logs) -{ - struct nilfs_segment_buffer *segbuf; - int err, ret = 0; - - list_for_each_entry(segbuf, logs, sb_list) { - err = nilfs_segbuf_wait(segbuf); - if (err && !ret) - ret = err; - } - return ret; -} - -/** - * nilfs_add_checksums_on_logs - add checksums on the logs - * @logs: list of segment buffers storing target logs - * @seed: checksum seed value - */ -void nilfs_add_checksums_on_logs(struct list_head *logs, u32 seed) -{ - struct nilfs_segment_buffer *segbuf; - - list_for_each_entry(segbuf, logs, sb_list) { - if (segbuf->sb_super_root) - nilfs_segbuf_fill_in_super_root_crc(segbuf, seed); - nilfs_segbuf_fill_in_segsum_crc(segbuf, seed); - nilfs_segbuf_fill_in_data_crc(segbuf, seed); - } -} - -/* - * BIO operations - */ -static void nilfs_end_bio_write(struct bio *bio, int err) -{ - const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); - struct nilfs_segment_buffer *segbuf = bio->bi_private; - - if (err == -EOPNOTSUPP) { - set_bit(BIO_EOPNOTSUPP, &bio->bi_flags); - bio_put(bio); - /* to be detected by submit_seg_bio() */ - } - - if (!uptodate) - atomic_inc(&segbuf->sb_err); - - bio_put(bio); - complete(&segbuf->sb_bio_event); -} - -static int nilfs_segbuf_submit_bio(struct nilfs_segment_buffer *segbuf, - struct nilfs_write_info *wi, int mode) -{ - struct bio *bio = wi->bio; - int err; - - if (segbuf->sb_nbio > 0 && - bdi_write_congested(segbuf->sb_super->s_bdi)) { - wait_for_completion(&segbuf->sb_bio_event); - segbuf->sb_nbio--; - if (unlikely(atomic_read(&segbuf->sb_err))) { - bio_put(bio); - err = -EIO; - goto failed; - } - } - - bio->bi_end_io = nilfs_end_bio_write; - bio->bi_private = segbuf; - bio_get(bio); - submit_bio(mode, bio); - if (bio_flagged(bio, BIO_EOPNOTSUPP)) { - bio_put(bio); - err = -EOPNOTSUPP; - goto failed; - } - segbuf->sb_nbio++; - bio_put(bio); - - wi->bio = NULL; - wi->rest_blocks -= wi->end - wi->start; - wi->nr_vecs = min(wi->max_pages, wi->rest_blocks); - wi->start = wi->end; - return 0; - - failed: - wi->bio = NULL; - return err; -} - -/** - * nilfs_alloc_seg_bio - allocate a new bio for writing log - * @nilfs: nilfs object - * @start: start block number of the bio - * @nr_vecs: request size of page vector. - * - * Return Value: On success, pointer to the struct bio is returned. - * On error, NULL is returned. - */ -static struct bio *nilfs_alloc_seg_bio(struct the_nilfs *nilfs, sector_t start, - int nr_vecs) -{ - struct bio *bio; - - bio = bio_alloc(GFP_NOIO, nr_vecs); - if (bio == NULL) { - while (!bio && (nr_vecs >>= 1)) - bio = bio_alloc(GFP_NOIO, nr_vecs); - } - if (likely(bio)) { - bio->bi_bdev = nilfs->ns_bdev; - bio->bi_sector = start << (nilfs->ns_blocksize_bits - 9); - } - return bio; -} - -static void nilfs_segbuf_prepare_write(struct nilfs_segment_buffer *segbuf, - struct nilfs_write_info *wi) -{ - wi->bio = NULL; - wi->rest_blocks = segbuf->sb_sum.nblocks; - wi->max_pages = bio_get_nr_vecs(wi->nilfs->ns_bdev); - wi->nr_vecs = min(wi->max_pages, wi->rest_blocks); - wi->start = wi->end = 0; - wi->blocknr = segbuf->sb_pseg_start; -} - -static int nilfs_segbuf_submit_bh(struct nilfs_segment_buffer *segbuf, - struct nilfs_write_info *wi, - struct buffer_head *bh, int mode) -{ - int len, err; - - BUG_ON(wi->nr_vecs <= 0); - repeat: - if (!wi->bio) { - wi->bio = nilfs_alloc_seg_bio(wi->nilfs, wi->blocknr + wi->end, - wi->nr_vecs); - if (unlikely(!wi->bio)) - return -ENOMEM; - } - - len = bio_add_page(wi->bio, bh->b_page, bh->b_size, bh_offset(bh)); - if (len == bh->b_size) { - wi->end++; - return 0; - } - /* bio is FULL */ - err = nilfs_segbuf_submit_bio(segbuf, wi, mode); - /* never submit current bh */ - if (likely(!err)) - goto repeat; - return err; -} - -/** - * nilfs_segbuf_write - submit write requests of a log - * @segbuf: buffer storing a log to be written - * @nilfs: nilfs object - * - * Return Value: On Success, 0 is returned. On Error, one of the following - * negative error code is returned. - * - * %-EIO - I/O error - * - * %-ENOMEM - Insufficient memory available. - */ -static int nilfs_segbuf_write(struct nilfs_segment_buffer *segbuf, - struct the_nilfs *nilfs) -{ - struct nilfs_write_info wi; - struct buffer_head *bh; - int res = 0, rw = WRITE; - - wi.nilfs = nilfs; - nilfs_segbuf_prepare_write(segbuf, &wi); - - list_for_each_entry(bh, &segbuf->sb_segsum_buffers, b_assoc_buffers) { - res = nilfs_segbuf_submit_bh(segbuf, &wi, bh, rw); - if (unlikely(res)) - goto failed_bio; - } - - list_for_each_entry(bh, &segbuf->sb_payload_buffers, b_assoc_buffers) { - res = nilfs_segbuf_submit_bh(segbuf, &wi, bh, rw); - if (unlikely(res)) - goto failed_bio; - } - - if (wi.bio) { - /* - * Last BIO is always sent through the following - * submission. - */ - rw |= REQ_SYNC; - res = nilfs_segbuf_submit_bio(segbuf, &wi, rw); - } - - failed_bio: - return res; -} - -/** - * nilfs_segbuf_wait - wait for completion of requested BIOs - * @segbuf: segment buffer - * - * Return Value: On Success, 0 is returned. On Error, one of the following - * negative error code is returned. - * - * %-EIO - I/O error - */ -static int nilfs_segbuf_wait(struct nilfs_segment_buffer *segbuf) -{ - int err = 0; - - if (!segbuf->sb_nbio) - return 0; - - do { - wait_for_completion(&segbuf->sb_bio_event); - } while (--segbuf->sb_nbio > 0); - - if (unlikely(atomic_read(&segbuf->sb_err) > 0)) { - printk(KERN_ERR "NILFS: IO error writing segment\n"); - err = -EIO; - } - return err; -} diff --git a/ANDROID_3.4.5/fs/nilfs2/segbuf.h b/ANDROID_3.4.5/fs/nilfs2/segbuf.h deleted file mode 100644 index b04f08cc..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/segbuf.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * segbuf.h - NILFS Segment buffer prototypes and definitions - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Ryusuke Konishi <ryusuke@osrg.net> - * - */ -#ifndef _NILFS_SEGBUF_H -#define _NILFS_SEGBUF_H - -#include <linux/fs.h> -#include <linux/buffer_head.h> -#include <linux/bio.h> -#include <linux/completion.h> - -/** - * struct nilfs_segsum_info - On-memory segment summary - * @flags: Flags - * @nfinfo: Number of file information structures - * @nblocks: Number of blocks included in the partial segment - * @nsumblk: Number of summary blocks - * @sumbytes: Byte count of segment summary - * @nfileblk: Total number of file blocks - * @seg_seq: Segment sequence number - * @cno: Checkpoint number - * @ctime: Creation time - * @next: Block number of the next full segment - */ -struct nilfs_segsum_info { - unsigned int flags; - unsigned long nfinfo; - unsigned long nblocks; - unsigned long nsumblk; - unsigned long sumbytes; - unsigned long nfileblk; - u64 seg_seq; - __u64 cno; - time_t ctime; - sector_t next; -}; - -/** - * struct nilfs_segment_buffer - Segment buffer - * @sb_super: back pointer to a superblock struct - * @sb_list: List head to chain this structure - * @sb_sum: On-memory segment summary - * @sb_segnum: Index number of the full segment - * @sb_nextnum: Index number of the next full segment - * @sb_fseg_start: Start block number of the full segment - * @sb_fseg_end: End block number of the full segment - * @sb_pseg_start: Disk block number of partial segment - * @sb_rest_blocks: Number of residual blocks in the current segment - * @sb_segsum_buffers: List of buffers for segment summaries - * @sb_payload_buffers: List of buffers for segment payload - * @sb_super_root: Pointer to buffer storing a super root block (if exists) - * @sb_nbio: Number of flying bio requests - * @sb_err: I/O error status - * @sb_bio_event: Completion event of log writing - */ -struct nilfs_segment_buffer { - struct super_block *sb_super; - struct list_head sb_list; - - /* Segment information */ - struct nilfs_segsum_info sb_sum; - __u64 sb_segnum; - __u64 sb_nextnum; - sector_t sb_fseg_start, sb_fseg_end; - sector_t sb_pseg_start; - unsigned sb_rest_blocks; - - /* Buffers */ - struct list_head sb_segsum_buffers; - struct list_head sb_payload_buffers; /* including super root */ - struct buffer_head *sb_super_root; - - /* io status */ - int sb_nbio; - atomic_t sb_err; - struct completion sb_bio_event; -}; - -#define NILFS_LIST_SEGBUF(head) \ - list_entry((head), struct nilfs_segment_buffer, sb_list) -#define NILFS_NEXT_SEGBUF(segbuf) NILFS_LIST_SEGBUF((segbuf)->sb_list.next) -#define NILFS_PREV_SEGBUF(segbuf) NILFS_LIST_SEGBUF((segbuf)->sb_list.prev) -#define NILFS_LAST_SEGBUF(head) NILFS_LIST_SEGBUF((head)->prev) -#define NILFS_FIRST_SEGBUF(head) NILFS_LIST_SEGBUF((head)->next) -#define NILFS_SEGBUF_IS_LAST(segbuf, head) ((segbuf)->sb_list.next == (head)) - -#define nilfs_for_each_segbuf_before(s, t, h) \ - for ((s) = NILFS_FIRST_SEGBUF(h); (s) != (t); \ - (s) = NILFS_NEXT_SEGBUF(s)) - -#define NILFS_SEGBUF_FIRST_BH(head) \ - (list_entry((head)->next, struct buffer_head, b_assoc_buffers)) -#define NILFS_SEGBUF_NEXT_BH(bh) \ - (list_entry((bh)->b_assoc_buffers.next, struct buffer_head, \ - b_assoc_buffers)) -#define NILFS_SEGBUF_BH_IS_LAST(bh, head) ((bh)->b_assoc_buffers.next == head) - -extern struct kmem_cache *nilfs_segbuf_cachep; - -struct nilfs_segment_buffer *nilfs_segbuf_new(struct super_block *); -void nilfs_segbuf_free(struct nilfs_segment_buffer *); -void nilfs_segbuf_map(struct nilfs_segment_buffer *, __u64, unsigned long, - struct the_nilfs *); -void nilfs_segbuf_map_cont(struct nilfs_segment_buffer *segbuf, - struct nilfs_segment_buffer *prev); -void nilfs_segbuf_set_next_segnum(struct nilfs_segment_buffer *, __u64, - struct the_nilfs *); -int nilfs_segbuf_reset(struct nilfs_segment_buffer *, unsigned, time_t, __u64); -int nilfs_segbuf_extend_segsum(struct nilfs_segment_buffer *); -int nilfs_segbuf_extend_payload(struct nilfs_segment_buffer *, - struct buffer_head **); -void nilfs_segbuf_fill_in_segsum(struct nilfs_segment_buffer *); - -static inline int nilfs_segbuf_simplex(struct nilfs_segment_buffer *segbuf) -{ - unsigned int flags = segbuf->sb_sum.flags; - - return (flags & (NILFS_SS_LOGBGN | NILFS_SS_LOGEND)) == - (NILFS_SS_LOGBGN | NILFS_SS_LOGEND); -} - -static inline int nilfs_segbuf_empty(struct nilfs_segment_buffer *segbuf) -{ - return segbuf->sb_sum.nblocks == segbuf->sb_sum.nsumblk; -} - -static inline void -nilfs_segbuf_add_segsum_buffer(struct nilfs_segment_buffer *segbuf, - struct buffer_head *bh) -{ - list_add_tail(&bh->b_assoc_buffers, &segbuf->sb_segsum_buffers); - segbuf->sb_sum.nblocks++; - segbuf->sb_sum.nsumblk++; -} - -static inline void -nilfs_segbuf_add_payload_buffer(struct nilfs_segment_buffer *segbuf, - struct buffer_head *bh) -{ - list_add_tail(&bh->b_assoc_buffers, &segbuf->sb_payload_buffers); - segbuf->sb_sum.nblocks++; -} - -static inline void -nilfs_segbuf_add_file_buffer(struct nilfs_segment_buffer *segbuf, - struct buffer_head *bh) -{ - get_bh(bh); - nilfs_segbuf_add_payload_buffer(segbuf, bh); - segbuf->sb_sum.nfileblk++; -} - -void nilfs_clear_logs(struct list_head *logs); -void nilfs_truncate_logs(struct list_head *logs, - struct nilfs_segment_buffer *last); -int nilfs_write_logs(struct list_head *logs, struct the_nilfs *nilfs); -int nilfs_wait_on_logs(struct list_head *logs); -void nilfs_add_checksums_on_logs(struct list_head *logs, u32 seed); - -static inline void nilfs_destroy_logs(struct list_head *logs) -{ - nilfs_truncate_logs(logs, NULL); -} - -#endif /* _NILFS_SEGBUF_H */ diff --git a/ANDROID_3.4.5/fs/nilfs2/segment.c b/ANDROID_3.4.5/fs/nilfs2/segment.c deleted file mode 100644 index 88e11fb3..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/segment.c +++ /dev/null @@ -1,2709 +0,0 @@ -/* - * segment.c - NILFS segment constructor. - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Ryusuke Konishi <ryusuke@osrg.net> - * - */ - -#include <linux/pagemap.h> -#include <linux/buffer_head.h> -#include <linux/writeback.h> -#include <linux/bio.h> -#include <linux/completion.h> -#include <linux/blkdev.h> -#include <linux/backing-dev.h> -#include <linux/freezer.h> -#include <linux/kthread.h> -#include <linux/crc32.h> -#include <linux/pagevec.h> -#include <linux/slab.h> -#include "nilfs.h" -#include "btnode.h" -#include "page.h" -#include "segment.h" -#include "sufile.h" -#include "cpfile.h" -#include "ifile.h" -#include "segbuf.h" - - -/* - * Segment constructor - */ -#define SC_N_INODEVEC 16 /* Size of locally allocated inode vector */ - -#define SC_MAX_SEGDELTA 64 /* Upper limit of the number of segments - appended in collection retry loop */ - -/* Construction mode */ -enum { - SC_LSEG_SR = 1, /* Make a logical segment having a super root */ - SC_LSEG_DSYNC, /* Flush data blocks of a given file and make - a logical segment without a super root */ - SC_FLUSH_FILE, /* Flush data files, leads to segment writes without - creating a checkpoint */ - SC_FLUSH_DAT, /* Flush DAT file. This also creates segments without - a checkpoint */ -}; - -/* Stage numbers of dirty block collection */ -enum { - NILFS_ST_INIT = 0, - NILFS_ST_GC, /* Collecting dirty blocks for GC */ - NILFS_ST_FILE, - NILFS_ST_IFILE, - NILFS_ST_CPFILE, - NILFS_ST_SUFILE, - NILFS_ST_DAT, - NILFS_ST_SR, /* Super root */ - NILFS_ST_DSYNC, /* Data sync blocks */ - NILFS_ST_DONE, -}; - -/* State flags of collection */ -#define NILFS_CF_NODE 0x0001 /* Collecting node blocks */ -#define NILFS_CF_IFILE_STARTED 0x0002 /* IFILE stage has started */ -#define NILFS_CF_SUFREED 0x0004 /* segment usages has been freed */ -#define NILFS_CF_HISTORY_MASK (NILFS_CF_IFILE_STARTED | NILFS_CF_SUFREED) - -/* Operations depending on the construction mode and file type */ -struct nilfs_sc_operations { - int (*collect_data)(struct nilfs_sc_info *, struct buffer_head *, - struct inode *); - int (*collect_node)(struct nilfs_sc_info *, struct buffer_head *, - struct inode *); - int (*collect_bmap)(struct nilfs_sc_info *, struct buffer_head *, - struct inode *); - void (*write_data_binfo)(struct nilfs_sc_info *, - struct nilfs_segsum_pointer *, - union nilfs_binfo *); - void (*write_node_binfo)(struct nilfs_sc_info *, - struct nilfs_segsum_pointer *, - union nilfs_binfo *); -}; - -/* - * Other definitions - */ -static void nilfs_segctor_start_timer(struct nilfs_sc_info *); -static void nilfs_segctor_do_flush(struct nilfs_sc_info *, int); -static void nilfs_segctor_do_immediate_flush(struct nilfs_sc_info *); -static void nilfs_dispose_list(struct the_nilfs *, struct list_head *, int); - -#define nilfs_cnt32_gt(a, b) \ - (typecheck(__u32, a) && typecheck(__u32, b) && \ - ((__s32)(b) - (__s32)(a) < 0)) -#define nilfs_cnt32_ge(a, b) \ - (typecheck(__u32, a) && typecheck(__u32, b) && \ - ((__s32)(a) - (__s32)(b) >= 0)) -#define nilfs_cnt32_lt(a, b) nilfs_cnt32_gt(b, a) -#define nilfs_cnt32_le(a, b) nilfs_cnt32_ge(b, a) - -static int nilfs_prepare_segment_lock(struct nilfs_transaction_info *ti) -{ - struct nilfs_transaction_info *cur_ti = current->journal_info; - void *save = NULL; - - if (cur_ti) { - if (cur_ti->ti_magic == NILFS_TI_MAGIC) - return ++cur_ti->ti_count; - else { - /* - * If journal_info field is occupied by other FS, - * it is saved and will be restored on - * nilfs_transaction_commit(). - */ - printk(KERN_WARNING - "NILFS warning: journal info from a different " - "FS\n"); - save = current->journal_info; - } - } - if (!ti) { - ti = kmem_cache_alloc(nilfs_transaction_cachep, GFP_NOFS); - if (!ti) - return -ENOMEM; - ti->ti_flags = NILFS_TI_DYNAMIC_ALLOC; - } else { - ti->ti_flags = 0; - } - ti->ti_count = 0; - ti->ti_save = save; - ti->ti_magic = NILFS_TI_MAGIC; - current->journal_info = ti; - return 0; -} - -/** - * nilfs_transaction_begin - start indivisible file operations. - * @sb: super block - * @ti: nilfs_transaction_info - * @vacancy_check: flags for vacancy rate checks - * - * nilfs_transaction_begin() acquires a reader/writer semaphore, called - * the segment semaphore, to make a segment construction and write tasks - * exclusive. The function is used with nilfs_transaction_commit() in pairs. - * The region enclosed by these two functions can be nested. To avoid a - * deadlock, the semaphore is only acquired or released in the outermost call. - * - * This function allocates a nilfs_transaction_info struct to keep context - * information on it. It is initialized and hooked onto the current task in - * the outermost call. If a pre-allocated struct is given to @ti, it is used - * instead; otherwise a new struct is assigned from a slab. - * - * When @vacancy_check flag is set, this function will check the amount of - * free space, and will wait for the GC to reclaim disk space if low capacity. - * - * Return Value: On success, 0 is returned. On error, one of the following - * negative error code is returned. - * - * %-ENOMEM - Insufficient memory available. - * - * %-ENOSPC - No space left on device - */ -int nilfs_transaction_begin(struct super_block *sb, - struct nilfs_transaction_info *ti, - int vacancy_check) -{ - struct the_nilfs *nilfs; - int ret = nilfs_prepare_segment_lock(ti); - - if (unlikely(ret < 0)) - return ret; - if (ret > 0) - return 0; - - vfs_check_frozen(sb, SB_FREEZE_WRITE); - - nilfs = sb->s_fs_info; - down_read(&nilfs->ns_segctor_sem); - if (vacancy_check && nilfs_near_disk_full(nilfs)) { - up_read(&nilfs->ns_segctor_sem); - ret = -ENOSPC; - goto failed; - } - return 0; - - failed: - ti = current->journal_info; - current->journal_info = ti->ti_save; - if (ti->ti_flags & NILFS_TI_DYNAMIC_ALLOC) - kmem_cache_free(nilfs_transaction_cachep, ti); - return ret; -} - -/** - * nilfs_transaction_commit - commit indivisible file operations. - * @sb: super block - * - * nilfs_transaction_commit() releases the read semaphore which is - * acquired by nilfs_transaction_begin(). This is only performed - * in outermost call of this function. If a commit flag is set, - * nilfs_transaction_commit() sets a timer to start the segment - * constructor. If a sync flag is set, it starts construction - * directly. - */ -int nilfs_transaction_commit(struct super_block *sb) -{ - struct nilfs_transaction_info *ti = current->journal_info; - struct the_nilfs *nilfs = sb->s_fs_info; - int err = 0; - - BUG_ON(ti == NULL || ti->ti_magic != NILFS_TI_MAGIC); - ti->ti_flags |= NILFS_TI_COMMIT; - if (ti->ti_count > 0) { - ti->ti_count--; - return 0; - } - if (nilfs->ns_writer) { - struct nilfs_sc_info *sci = nilfs->ns_writer; - - if (ti->ti_flags & NILFS_TI_COMMIT) - nilfs_segctor_start_timer(sci); - if (atomic_read(&nilfs->ns_ndirtyblks) > sci->sc_watermark) - nilfs_segctor_do_flush(sci, 0); - } - up_read(&nilfs->ns_segctor_sem); - current->journal_info = ti->ti_save; - - if (ti->ti_flags & NILFS_TI_SYNC) - err = nilfs_construct_segment(sb); - if (ti->ti_flags & NILFS_TI_DYNAMIC_ALLOC) - kmem_cache_free(nilfs_transaction_cachep, ti); - return err; -} - -void nilfs_transaction_abort(struct super_block *sb) -{ - struct nilfs_transaction_info *ti = current->journal_info; - struct the_nilfs *nilfs = sb->s_fs_info; - - BUG_ON(ti == NULL || ti->ti_magic != NILFS_TI_MAGIC); - if (ti->ti_count > 0) { - ti->ti_count--; - return; - } - up_read(&nilfs->ns_segctor_sem); - - current->journal_info = ti->ti_save; - if (ti->ti_flags & NILFS_TI_DYNAMIC_ALLOC) - kmem_cache_free(nilfs_transaction_cachep, ti); -} - -void nilfs_relax_pressure_in_lock(struct super_block *sb) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct nilfs_sc_info *sci = nilfs->ns_writer; - - if (!sci || !sci->sc_flush_request) - return; - - set_bit(NILFS_SC_PRIOR_FLUSH, &sci->sc_flags); - up_read(&nilfs->ns_segctor_sem); - - down_write(&nilfs->ns_segctor_sem); - if (sci->sc_flush_request && - test_bit(NILFS_SC_PRIOR_FLUSH, &sci->sc_flags)) { - struct nilfs_transaction_info *ti = current->journal_info; - - ti->ti_flags |= NILFS_TI_WRITER; - nilfs_segctor_do_immediate_flush(sci); - ti->ti_flags &= ~NILFS_TI_WRITER; - } - downgrade_write(&nilfs->ns_segctor_sem); -} - -static void nilfs_transaction_lock(struct super_block *sb, - struct nilfs_transaction_info *ti, - int gcflag) -{ - struct nilfs_transaction_info *cur_ti = current->journal_info; - struct the_nilfs *nilfs = sb->s_fs_info; - struct nilfs_sc_info *sci = nilfs->ns_writer; - - WARN_ON(cur_ti); - ti->ti_flags = NILFS_TI_WRITER; - ti->ti_count = 0; - ti->ti_save = cur_ti; - ti->ti_magic = NILFS_TI_MAGIC; - INIT_LIST_HEAD(&ti->ti_garbage); - current->journal_info = ti; - - for (;;) { - down_write(&nilfs->ns_segctor_sem); - if (!test_bit(NILFS_SC_PRIOR_FLUSH, &sci->sc_flags)) - break; - - nilfs_segctor_do_immediate_flush(sci); - - up_write(&nilfs->ns_segctor_sem); - yield(); - } - if (gcflag) - ti->ti_flags |= NILFS_TI_GC; -} - -static void nilfs_transaction_unlock(struct super_block *sb) -{ - struct nilfs_transaction_info *ti = current->journal_info; - struct the_nilfs *nilfs = sb->s_fs_info; - - BUG_ON(ti == NULL || ti->ti_magic != NILFS_TI_MAGIC); - BUG_ON(ti->ti_count > 0); - - up_write(&nilfs->ns_segctor_sem); - current->journal_info = ti->ti_save; - if (!list_empty(&ti->ti_garbage)) - nilfs_dispose_list(nilfs, &ti->ti_garbage, 0); -} - -static void *nilfs_segctor_map_segsum_entry(struct nilfs_sc_info *sci, - struct nilfs_segsum_pointer *ssp, - unsigned bytes) -{ - struct nilfs_segment_buffer *segbuf = sci->sc_curseg; - unsigned blocksize = sci->sc_super->s_blocksize; - void *p; - - if (unlikely(ssp->offset + bytes > blocksize)) { - ssp->offset = 0; - BUG_ON(NILFS_SEGBUF_BH_IS_LAST(ssp->bh, - &segbuf->sb_segsum_buffers)); - ssp->bh = NILFS_SEGBUF_NEXT_BH(ssp->bh); - } - p = ssp->bh->b_data + ssp->offset; - ssp->offset += bytes; - return p; -} - -/** - * nilfs_segctor_reset_segment_buffer - reset the current segment buffer - * @sci: nilfs_sc_info - */ -static int nilfs_segctor_reset_segment_buffer(struct nilfs_sc_info *sci) -{ - struct nilfs_segment_buffer *segbuf = sci->sc_curseg; - struct buffer_head *sumbh; - unsigned sumbytes; - unsigned flags = 0; - int err; - - if (nilfs_doing_gc()) - flags = NILFS_SS_GC; - err = nilfs_segbuf_reset(segbuf, flags, sci->sc_seg_ctime, sci->sc_cno); - if (unlikely(err)) - return err; - - sumbh = NILFS_SEGBUF_FIRST_BH(&segbuf->sb_segsum_buffers); - sumbytes = segbuf->sb_sum.sumbytes; - sci->sc_finfo_ptr.bh = sumbh; sci->sc_finfo_ptr.offset = sumbytes; - sci->sc_binfo_ptr.bh = sumbh; sci->sc_binfo_ptr.offset = sumbytes; - sci->sc_blk_cnt = sci->sc_datablk_cnt = 0; - return 0; -} - -static int nilfs_segctor_feed_segment(struct nilfs_sc_info *sci) -{ - sci->sc_nblk_this_inc += sci->sc_curseg->sb_sum.nblocks; - if (NILFS_SEGBUF_IS_LAST(sci->sc_curseg, &sci->sc_segbufs)) - return -E2BIG; /* The current segment is filled up - (internal code) */ - sci->sc_curseg = NILFS_NEXT_SEGBUF(sci->sc_curseg); - return nilfs_segctor_reset_segment_buffer(sci); -} - -static int nilfs_segctor_add_super_root(struct nilfs_sc_info *sci) -{ - struct nilfs_segment_buffer *segbuf = sci->sc_curseg; - int err; - - if (segbuf->sb_sum.nblocks >= segbuf->sb_rest_blocks) { - err = nilfs_segctor_feed_segment(sci); - if (err) - return err; - segbuf = sci->sc_curseg; - } - err = nilfs_segbuf_extend_payload(segbuf, &segbuf->sb_super_root); - if (likely(!err)) - segbuf->sb_sum.flags |= NILFS_SS_SR; - return err; -} - -/* - * Functions for making segment summary and payloads - */ -static int nilfs_segctor_segsum_block_required( - struct nilfs_sc_info *sci, const struct nilfs_segsum_pointer *ssp, - unsigned binfo_size) -{ - unsigned blocksize = sci->sc_super->s_blocksize; - /* Size of finfo and binfo is enough small against blocksize */ - - return ssp->offset + binfo_size + - (!sci->sc_blk_cnt ? sizeof(struct nilfs_finfo) : 0) > - blocksize; -} - -static void nilfs_segctor_begin_finfo(struct nilfs_sc_info *sci, - struct inode *inode) -{ - sci->sc_curseg->sb_sum.nfinfo++; - sci->sc_binfo_ptr = sci->sc_finfo_ptr; - nilfs_segctor_map_segsum_entry( - sci, &sci->sc_binfo_ptr, sizeof(struct nilfs_finfo)); - - if (NILFS_I(inode)->i_root && - !test_bit(NILFS_SC_HAVE_DELTA, &sci->sc_flags)) - set_bit(NILFS_SC_HAVE_DELTA, &sci->sc_flags); - /* skip finfo */ -} - -static void nilfs_segctor_end_finfo(struct nilfs_sc_info *sci, - struct inode *inode) -{ - struct nilfs_finfo *finfo; - struct nilfs_inode_info *ii; - struct nilfs_segment_buffer *segbuf; - __u64 cno; - - if (sci->sc_blk_cnt == 0) - return; - - ii = NILFS_I(inode); - - if (test_bit(NILFS_I_GCINODE, &ii->i_state)) - cno = ii->i_cno; - else if (NILFS_ROOT_METADATA_FILE(inode->i_ino)) - cno = 0; - else - cno = sci->sc_cno; - - finfo = nilfs_segctor_map_segsum_entry(sci, &sci->sc_finfo_ptr, - sizeof(*finfo)); - finfo->fi_ino = cpu_to_le64(inode->i_ino); - finfo->fi_nblocks = cpu_to_le32(sci->sc_blk_cnt); - finfo->fi_ndatablk = cpu_to_le32(sci->sc_datablk_cnt); - finfo->fi_cno = cpu_to_le64(cno); - - segbuf = sci->sc_curseg; - segbuf->sb_sum.sumbytes = sci->sc_binfo_ptr.offset + - sci->sc_super->s_blocksize * (segbuf->sb_sum.nsumblk - 1); - sci->sc_finfo_ptr = sci->sc_binfo_ptr; - sci->sc_blk_cnt = sci->sc_datablk_cnt = 0; -} - -static int nilfs_segctor_add_file_block(struct nilfs_sc_info *sci, - struct buffer_head *bh, - struct inode *inode, - unsigned binfo_size) -{ - struct nilfs_segment_buffer *segbuf; - int required, err = 0; - - retry: - segbuf = sci->sc_curseg; - required = nilfs_segctor_segsum_block_required( - sci, &sci->sc_binfo_ptr, binfo_size); - if (segbuf->sb_sum.nblocks + required + 1 > segbuf->sb_rest_blocks) { - nilfs_segctor_end_finfo(sci, inode); - err = nilfs_segctor_feed_segment(sci); - if (err) - return err; - goto retry; - } - if (unlikely(required)) { - err = nilfs_segbuf_extend_segsum(segbuf); - if (unlikely(err)) - goto failed; - } - if (sci->sc_blk_cnt == 0) - nilfs_segctor_begin_finfo(sci, inode); - - nilfs_segctor_map_segsum_entry(sci, &sci->sc_binfo_ptr, binfo_size); - /* Substitution to vblocknr is delayed until update_blocknr() */ - nilfs_segbuf_add_file_buffer(segbuf, bh); - sci->sc_blk_cnt++; - failed: - return err; -} - -/* - * Callback functions that enumerate, mark, and collect dirty blocks - */ -static int nilfs_collect_file_data(struct nilfs_sc_info *sci, - struct buffer_head *bh, struct inode *inode) -{ - int err; - - err = nilfs_bmap_propagate(NILFS_I(inode)->i_bmap, bh); - if (err < 0) - return err; - - err = nilfs_segctor_add_file_block(sci, bh, inode, - sizeof(struct nilfs_binfo_v)); - if (!err) - sci->sc_datablk_cnt++; - return err; -} - -static int nilfs_collect_file_node(struct nilfs_sc_info *sci, - struct buffer_head *bh, - struct inode *inode) -{ - return nilfs_bmap_propagate(NILFS_I(inode)->i_bmap, bh); -} - -static int nilfs_collect_file_bmap(struct nilfs_sc_info *sci, - struct buffer_head *bh, - struct inode *inode) -{ - WARN_ON(!buffer_dirty(bh)); - return nilfs_segctor_add_file_block(sci, bh, inode, sizeof(__le64)); -} - -static void nilfs_write_file_data_binfo(struct nilfs_sc_info *sci, - struct nilfs_segsum_pointer *ssp, - union nilfs_binfo *binfo) -{ - struct nilfs_binfo_v *binfo_v = nilfs_segctor_map_segsum_entry( - sci, ssp, sizeof(*binfo_v)); - *binfo_v = binfo->bi_v; -} - -static void nilfs_write_file_node_binfo(struct nilfs_sc_info *sci, - struct nilfs_segsum_pointer *ssp, - union nilfs_binfo *binfo) -{ - __le64 *vblocknr = nilfs_segctor_map_segsum_entry( - sci, ssp, sizeof(*vblocknr)); - *vblocknr = binfo->bi_v.bi_vblocknr; -} - -static struct nilfs_sc_operations nilfs_sc_file_ops = { - .collect_data = nilfs_collect_file_data, - .collect_node = nilfs_collect_file_node, - .collect_bmap = nilfs_collect_file_bmap, - .write_data_binfo = nilfs_write_file_data_binfo, - .write_node_binfo = nilfs_write_file_node_binfo, -}; - -static int nilfs_collect_dat_data(struct nilfs_sc_info *sci, - struct buffer_head *bh, struct inode *inode) -{ - int err; - - err = nilfs_bmap_propagate(NILFS_I(inode)->i_bmap, bh); - if (err < 0) - return err; - - err = nilfs_segctor_add_file_block(sci, bh, inode, sizeof(__le64)); - if (!err) - sci->sc_datablk_cnt++; - return err; -} - -static int nilfs_collect_dat_bmap(struct nilfs_sc_info *sci, - struct buffer_head *bh, struct inode *inode) -{ - WARN_ON(!buffer_dirty(bh)); - return nilfs_segctor_add_file_block(sci, bh, inode, - sizeof(struct nilfs_binfo_dat)); -} - -static void nilfs_write_dat_data_binfo(struct nilfs_sc_info *sci, - struct nilfs_segsum_pointer *ssp, - union nilfs_binfo *binfo) -{ - __le64 *blkoff = nilfs_segctor_map_segsum_entry(sci, ssp, - sizeof(*blkoff)); - *blkoff = binfo->bi_dat.bi_blkoff; -} - -static void nilfs_write_dat_node_binfo(struct nilfs_sc_info *sci, - struct nilfs_segsum_pointer *ssp, - union nilfs_binfo *binfo) -{ - struct nilfs_binfo_dat *binfo_dat = - nilfs_segctor_map_segsum_entry(sci, ssp, sizeof(*binfo_dat)); - *binfo_dat = binfo->bi_dat; -} - -static struct nilfs_sc_operations nilfs_sc_dat_ops = { - .collect_data = nilfs_collect_dat_data, - .collect_node = nilfs_collect_file_node, - .collect_bmap = nilfs_collect_dat_bmap, - .write_data_binfo = nilfs_write_dat_data_binfo, - .write_node_binfo = nilfs_write_dat_node_binfo, -}; - -static struct nilfs_sc_operations nilfs_sc_dsync_ops = { - .collect_data = nilfs_collect_file_data, - .collect_node = NULL, - .collect_bmap = NULL, - .write_data_binfo = nilfs_write_file_data_binfo, - .write_node_binfo = NULL, -}; - -static size_t nilfs_lookup_dirty_data_buffers(struct inode *inode, - struct list_head *listp, - size_t nlimit, - loff_t start, loff_t end) -{ - struct address_space *mapping = inode->i_mapping; - struct pagevec pvec; - pgoff_t index = 0, last = ULONG_MAX; - size_t ndirties = 0; - int i; - - if (unlikely(start != 0 || end != LLONG_MAX)) { - /* - * A valid range is given for sync-ing data pages. The - * range is rounded to per-page; extra dirty buffers - * may be included if blocksize < pagesize. - */ - index = start >> PAGE_SHIFT; - last = end >> PAGE_SHIFT; - } - pagevec_init(&pvec, 0); - repeat: - if (unlikely(index > last) || - !pagevec_lookup_tag(&pvec, mapping, &index, PAGECACHE_TAG_DIRTY, - min_t(pgoff_t, last - index, - PAGEVEC_SIZE - 1) + 1)) - return ndirties; - - for (i = 0; i < pagevec_count(&pvec); i++) { - struct buffer_head *bh, *head; - struct page *page = pvec.pages[i]; - - if (unlikely(page->index > last)) - break; - - lock_page(page); - if (!page_has_buffers(page)) - create_empty_buffers(page, 1 << inode->i_blkbits, 0); - unlock_page(page); - - bh = head = page_buffers(page); - do { - if (!buffer_dirty(bh)) - continue; - get_bh(bh); - list_add_tail(&bh->b_assoc_buffers, listp); - ndirties++; - if (unlikely(ndirties >= nlimit)) { - pagevec_release(&pvec); - cond_resched(); - return ndirties; - } - } while (bh = bh->b_this_page, bh != head); - } - pagevec_release(&pvec); - cond_resched(); - goto repeat; -} - -static void nilfs_lookup_dirty_node_buffers(struct inode *inode, - struct list_head *listp) -{ - struct nilfs_inode_info *ii = NILFS_I(inode); - struct address_space *mapping = &ii->i_btnode_cache; - struct pagevec pvec; - struct buffer_head *bh, *head; - unsigned int i; - pgoff_t index = 0; - - pagevec_init(&pvec, 0); - - while (pagevec_lookup_tag(&pvec, mapping, &index, PAGECACHE_TAG_DIRTY, - PAGEVEC_SIZE)) { - for (i = 0; i < pagevec_count(&pvec); i++) { - bh = head = page_buffers(pvec.pages[i]); - do { - if (buffer_dirty(bh)) { - get_bh(bh); - list_add_tail(&bh->b_assoc_buffers, - listp); - } - bh = bh->b_this_page; - } while (bh != head); - } - pagevec_release(&pvec); - cond_resched(); - } -} - -static void nilfs_dispose_list(struct the_nilfs *nilfs, - struct list_head *head, int force) -{ - struct nilfs_inode_info *ii, *n; - struct nilfs_inode_info *ivec[SC_N_INODEVEC], **pii; - unsigned nv = 0; - - while (!list_empty(head)) { - spin_lock(&nilfs->ns_inode_lock); - list_for_each_entry_safe(ii, n, head, i_dirty) { - list_del_init(&ii->i_dirty); - if (force) { - if (unlikely(ii->i_bh)) { - brelse(ii->i_bh); - ii->i_bh = NULL; - } - } else if (test_bit(NILFS_I_DIRTY, &ii->i_state)) { - set_bit(NILFS_I_QUEUED, &ii->i_state); - list_add_tail(&ii->i_dirty, - &nilfs->ns_dirty_files); - continue; - } - ivec[nv++] = ii; - if (nv == SC_N_INODEVEC) - break; - } - spin_unlock(&nilfs->ns_inode_lock); - - for (pii = ivec; nv > 0; pii++, nv--) - iput(&(*pii)->vfs_inode); - } -} - -static int nilfs_test_metadata_dirty(struct the_nilfs *nilfs, - struct nilfs_root *root) -{ - int ret = 0; - - if (nilfs_mdt_fetch_dirty(root->ifile)) - ret++; - if (nilfs_mdt_fetch_dirty(nilfs->ns_cpfile)) - ret++; - if (nilfs_mdt_fetch_dirty(nilfs->ns_sufile)) - ret++; - if ((ret || nilfs_doing_gc()) && nilfs_mdt_fetch_dirty(nilfs->ns_dat)) - ret++; - return ret; -} - -static int nilfs_segctor_clean(struct nilfs_sc_info *sci) -{ - return list_empty(&sci->sc_dirty_files) && - !test_bit(NILFS_SC_DIRTY, &sci->sc_flags) && - sci->sc_nfreesegs == 0 && - (!nilfs_doing_gc() || list_empty(&sci->sc_gc_inodes)); -} - -static int nilfs_segctor_confirm(struct nilfs_sc_info *sci) -{ - struct the_nilfs *nilfs = sci->sc_super->s_fs_info; - int ret = 0; - - if (nilfs_test_metadata_dirty(nilfs, sci->sc_root)) - set_bit(NILFS_SC_DIRTY, &sci->sc_flags); - - spin_lock(&nilfs->ns_inode_lock); - if (list_empty(&nilfs->ns_dirty_files) && nilfs_segctor_clean(sci)) - ret++; - - spin_unlock(&nilfs->ns_inode_lock); - return ret; -} - -static void nilfs_segctor_clear_metadata_dirty(struct nilfs_sc_info *sci) -{ - struct the_nilfs *nilfs = sci->sc_super->s_fs_info; - - nilfs_mdt_clear_dirty(sci->sc_root->ifile); - nilfs_mdt_clear_dirty(nilfs->ns_cpfile); - nilfs_mdt_clear_dirty(nilfs->ns_sufile); - nilfs_mdt_clear_dirty(nilfs->ns_dat); -} - -static int nilfs_segctor_create_checkpoint(struct nilfs_sc_info *sci) -{ - struct the_nilfs *nilfs = sci->sc_super->s_fs_info; - struct buffer_head *bh_cp; - struct nilfs_checkpoint *raw_cp; - int err; - - /* XXX: this interface will be changed */ - err = nilfs_cpfile_get_checkpoint(nilfs->ns_cpfile, nilfs->ns_cno, 1, - &raw_cp, &bh_cp); - if (likely(!err)) { - /* The following code is duplicated with cpfile. But, it is - needed to collect the checkpoint even if it was not newly - created */ - mark_buffer_dirty(bh_cp); - nilfs_mdt_mark_dirty(nilfs->ns_cpfile); - nilfs_cpfile_put_checkpoint( - nilfs->ns_cpfile, nilfs->ns_cno, bh_cp); - } else - WARN_ON(err == -EINVAL || err == -ENOENT); - - return err; -} - -static int nilfs_segctor_fill_in_checkpoint(struct nilfs_sc_info *sci) -{ - struct the_nilfs *nilfs = sci->sc_super->s_fs_info; - struct buffer_head *bh_cp; - struct nilfs_checkpoint *raw_cp; - int err; - - err = nilfs_cpfile_get_checkpoint(nilfs->ns_cpfile, nilfs->ns_cno, 0, - &raw_cp, &bh_cp); - if (unlikely(err)) { - WARN_ON(err == -EINVAL || err == -ENOENT); - goto failed_ibh; - } - raw_cp->cp_snapshot_list.ssl_next = 0; - raw_cp->cp_snapshot_list.ssl_prev = 0; - raw_cp->cp_inodes_count = - cpu_to_le64(atomic_read(&sci->sc_root->inodes_count)); - raw_cp->cp_blocks_count = - cpu_to_le64(atomic_read(&sci->sc_root->blocks_count)); - raw_cp->cp_nblk_inc = - cpu_to_le64(sci->sc_nblk_inc + sci->sc_nblk_this_inc); - raw_cp->cp_create = cpu_to_le64(sci->sc_seg_ctime); - raw_cp->cp_cno = cpu_to_le64(nilfs->ns_cno); - - if (test_bit(NILFS_SC_HAVE_DELTA, &sci->sc_flags)) - nilfs_checkpoint_clear_minor(raw_cp); - else - nilfs_checkpoint_set_minor(raw_cp); - - nilfs_write_inode_common(sci->sc_root->ifile, - &raw_cp->cp_ifile_inode, 1); - nilfs_cpfile_put_checkpoint(nilfs->ns_cpfile, nilfs->ns_cno, bh_cp); - return 0; - - failed_ibh: - return err; -} - -static void nilfs_fill_in_file_bmap(struct inode *ifile, - struct nilfs_inode_info *ii) - -{ - struct buffer_head *ibh; - struct nilfs_inode *raw_inode; - - if (test_bit(NILFS_I_BMAP, &ii->i_state)) { - ibh = ii->i_bh; - BUG_ON(!ibh); - raw_inode = nilfs_ifile_map_inode(ifile, ii->vfs_inode.i_ino, - ibh); - nilfs_bmap_write(ii->i_bmap, raw_inode); - nilfs_ifile_unmap_inode(ifile, ii->vfs_inode.i_ino, ibh); - } -} - -static void nilfs_segctor_fill_in_file_bmap(struct nilfs_sc_info *sci) -{ - struct nilfs_inode_info *ii; - - list_for_each_entry(ii, &sci->sc_dirty_files, i_dirty) { - nilfs_fill_in_file_bmap(sci->sc_root->ifile, ii); - set_bit(NILFS_I_COLLECTED, &ii->i_state); - } -} - -static void nilfs_segctor_fill_in_super_root(struct nilfs_sc_info *sci, - struct the_nilfs *nilfs) -{ - struct buffer_head *bh_sr; - struct nilfs_super_root *raw_sr; - unsigned isz, srsz; - - bh_sr = NILFS_LAST_SEGBUF(&sci->sc_segbufs)->sb_super_root; - raw_sr = (struct nilfs_super_root *)bh_sr->b_data; - isz = nilfs->ns_inode_size; - srsz = NILFS_SR_BYTES(isz); - - raw_sr->sr_bytes = cpu_to_le16(srsz); - raw_sr->sr_nongc_ctime - = cpu_to_le64(nilfs_doing_gc() ? - nilfs->ns_nongc_ctime : sci->sc_seg_ctime); - raw_sr->sr_flags = 0; - - nilfs_write_inode_common(nilfs->ns_dat, (void *)raw_sr + - NILFS_SR_DAT_OFFSET(isz), 1); - nilfs_write_inode_common(nilfs->ns_cpfile, (void *)raw_sr + - NILFS_SR_CPFILE_OFFSET(isz), 1); - nilfs_write_inode_common(nilfs->ns_sufile, (void *)raw_sr + - NILFS_SR_SUFILE_OFFSET(isz), 1); - memset((void *)raw_sr + srsz, 0, nilfs->ns_blocksize - srsz); -} - -static void nilfs_redirty_inodes(struct list_head *head) -{ - struct nilfs_inode_info *ii; - - list_for_each_entry(ii, head, i_dirty) { - if (test_bit(NILFS_I_COLLECTED, &ii->i_state)) - clear_bit(NILFS_I_COLLECTED, &ii->i_state); - } -} - -static void nilfs_drop_collected_inodes(struct list_head *head) -{ - struct nilfs_inode_info *ii; - - list_for_each_entry(ii, head, i_dirty) { - if (!test_and_clear_bit(NILFS_I_COLLECTED, &ii->i_state)) - continue; - - clear_bit(NILFS_I_INODE_DIRTY, &ii->i_state); - set_bit(NILFS_I_UPDATED, &ii->i_state); - } -} - -static int nilfs_segctor_apply_buffers(struct nilfs_sc_info *sci, - struct inode *inode, - struct list_head *listp, - int (*collect)(struct nilfs_sc_info *, - struct buffer_head *, - struct inode *)) -{ - struct buffer_head *bh, *n; - int err = 0; - - if (collect) { - list_for_each_entry_safe(bh, n, listp, b_assoc_buffers) { - list_del_init(&bh->b_assoc_buffers); - err = collect(sci, bh, inode); - brelse(bh); - if (unlikely(err)) - goto dispose_buffers; - } - return 0; - } - - dispose_buffers: - while (!list_empty(listp)) { - bh = list_first_entry(listp, struct buffer_head, - b_assoc_buffers); - list_del_init(&bh->b_assoc_buffers); - brelse(bh); - } - return err; -} - -static size_t nilfs_segctor_buffer_rest(struct nilfs_sc_info *sci) -{ - /* Remaining number of blocks within segment buffer */ - return sci->sc_segbuf_nblocks - - (sci->sc_nblk_this_inc + sci->sc_curseg->sb_sum.nblocks); -} - -static int nilfs_segctor_scan_file(struct nilfs_sc_info *sci, - struct inode *inode, - struct nilfs_sc_operations *sc_ops) -{ - LIST_HEAD(data_buffers); - LIST_HEAD(node_buffers); - int err; - - if (!(sci->sc_stage.flags & NILFS_CF_NODE)) { - size_t n, rest = nilfs_segctor_buffer_rest(sci); - - n = nilfs_lookup_dirty_data_buffers( - inode, &data_buffers, rest + 1, 0, LLONG_MAX); - if (n > rest) { - err = nilfs_segctor_apply_buffers( - sci, inode, &data_buffers, - sc_ops->collect_data); - BUG_ON(!err); /* always receive -E2BIG or true error */ - goto break_or_fail; - } - } - nilfs_lookup_dirty_node_buffers(inode, &node_buffers); - - if (!(sci->sc_stage.flags & NILFS_CF_NODE)) { - err = nilfs_segctor_apply_buffers( - sci, inode, &data_buffers, sc_ops->collect_data); - if (unlikely(err)) { - /* dispose node list */ - nilfs_segctor_apply_buffers( - sci, inode, &node_buffers, NULL); - goto break_or_fail; - } - sci->sc_stage.flags |= NILFS_CF_NODE; - } - /* Collect node */ - err = nilfs_segctor_apply_buffers( - sci, inode, &node_buffers, sc_ops->collect_node); - if (unlikely(err)) - goto break_or_fail; - - nilfs_bmap_lookup_dirty_buffers(NILFS_I(inode)->i_bmap, &node_buffers); - err = nilfs_segctor_apply_buffers( - sci, inode, &node_buffers, sc_ops->collect_bmap); - if (unlikely(err)) - goto break_or_fail; - - nilfs_segctor_end_finfo(sci, inode); - sci->sc_stage.flags &= ~NILFS_CF_NODE; - - break_or_fail: - return err; -} - -static int nilfs_segctor_scan_file_dsync(struct nilfs_sc_info *sci, - struct inode *inode) -{ - LIST_HEAD(data_buffers); - size_t n, rest = nilfs_segctor_buffer_rest(sci); - int err; - - n = nilfs_lookup_dirty_data_buffers(inode, &data_buffers, rest + 1, - sci->sc_dsync_start, - sci->sc_dsync_end); - - err = nilfs_segctor_apply_buffers(sci, inode, &data_buffers, - nilfs_collect_file_data); - if (!err) { - nilfs_segctor_end_finfo(sci, inode); - BUG_ON(n > rest); - /* always receive -E2BIG or true error if n > rest */ - } - return err; -} - -static int nilfs_segctor_collect_blocks(struct nilfs_sc_info *sci, int mode) -{ - struct the_nilfs *nilfs = sci->sc_super->s_fs_info; - struct list_head *head; - struct nilfs_inode_info *ii; - size_t ndone; - int err = 0; - - switch (sci->sc_stage.scnt) { - case NILFS_ST_INIT: - /* Pre-processes */ - sci->sc_stage.flags = 0; - - if (!test_bit(NILFS_SC_UNCLOSED, &sci->sc_flags)) { - sci->sc_nblk_inc = 0; - sci->sc_curseg->sb_sum.flags = NILFS_SS_LOGBGN; - if (mode == SC_LSEG_DSYNC) { - sci->sc_stage.scnt = NILFS_ST_DSYNC; - goto dsync_mode; - } - } - - sci->sc_stage.dirty_file_ptr = NULL; - sci->sc_stage.gc_inode_ptr = NULL; - if (mode == SC_FLUSH_DAT) { - sci->sc_stage.scnt = NILFS_ST_DAT; - goto dat_stage; - } - sci->sc_stage.scnt++; /* Fall through */ - case NILFS_ST_GC: - if (nilfs_doing_gc()) { - head = &sci->sc_gc_inodes; - ii = list_prepare_entry(sci->sc_stage.gc_inode_ptr, - head, i_dirty); - list_for_each_entry_continue(ii, head, i_dirty) { - err = nilfs_segctor_scan_file( - sci, &ii->vfs_inode, - &nilfs_sc_file_ops); - if (unlikely(err)) { - sci->sc_stage.gc_inode_ptr = list_entry( - ii->i_dirty.prev, - struct nilfs_inode_info, - i_dirty); - goto break_or_fail; - } - set_bit(NILFS_I_COLLECTED, &ii->i_state); - } - sci->sc_stage.gc_inode_ptr = NULL; - } - sci->sc_stage.scnt++; /* Fall through */ - case NILFS_ST_FILE: - head = &sci->sc_dirty_files; - ii = list_prepare_entry(sci->sc_stage.dirty_file_ptr, head, - i_dirty); - list_for_each_entry_continue(ii, head, i_dirty) { - clear_bit(NILFS_I_DIRTY, &ii->i_state); - - err = nilfs_segctor_scan_file(sci, &ii->vfs_inode, - &nilfs_sc_file_ops); - if (unlikely(err)) { - sci->sc_stage.dirty_file_ptr = - list_entry(ii->i_dirty.prev, - struct nilfs_inode_info, - i_dirty); - goto break_or_fail; - } - /* sci->sc_stage.dirty_file_ptr = NILFS_I(inode); */ - /* XXX: required ? */ - } - sci->sc_stage.dirty_file_ptr = NULL; - if (mode == SC_FLUSH_FILE) { - sci->sc_stage.scnt = NILFS_ST_DONE; - return 0; - } - sci->sc_stage.scnt++; - sci->sc_stage.flags |= NILFS_CF_IFILE_STARTED; - /* Fall through */ - case NILFS_ST_IFILE: - err = nilfs_segctor_scan_file(sci, sci->sc_root->ifile, - &nilfs_sc_file_ops); - if (unlikely(err)) - break; - sci->sc_stage.scnt++; - /* Creating a checkpoint */ - err = nilfs_segctor_create_checkpoint(sci); - if (unlikely(err)) - break; - /* Fall through */ - case NILFS_ST_CPFILE: - err = nilfs_segctor_scan_file(sci, nilfs->ns_cpfile, - &nilfs_sc_file_ops); - if (unlikely(err)) - break; - sci->sc_stage.scnt++; /* Fall through */ - case NILFS_ST_SUFILE: - err = nilfs_sufile_freev(nilfs->ns_sufile, sci->sc_freesegs, - sci->sc_nfreesegs, &ndone); - if (unlikely(err)) { - nilfs_sufile_cancel_freev(nilfs->ns_sufile, - sci->sc_freesegs, ndone, - NULL); - break; - } - sci->sc_stage.flags |= NILFS_CF_SUFREED; - - err = nilfs_segctor_scan_file(sci, nilfs->ns_sufile, - &nilfs_sc_file_ops); - if (unlikely(err)) - break; - sci->sc_stage.scnt++; /* Fall through */ - case NILFS_ST_DAT: - dat_stage: - err = nilfs_segctor_scan_file(sci, nilfs->ns_dat, - &nilfs_sc_dat_ops); - if (unlikely(err)) - break; - if (mode == SC_FLUSH_DAT) { - sci->sc_stage.scnt = NILFS_ST_DONE; - return 0; - } - sci->sc_stage.scnt++; /* Fall through */ - case NILFS_ST_SR: - if (mode == SC_LSEG_SR) { - /* Appending a super root */ - err = nilfs_segctor_add_super_root(sci); - if (unlikely(err)) - break; - } - /* End of a logical segment */ - sci->sc_curseg->sb_sum.flags |= NILFS_SS_LOGEND; - sci->sc_stage.scnt = NILFS_ST_DONE; - return 0; - case NILFS_ST_DSYNC: - dsync_mode: - sci->sc_curseg->sb_sum.flags |= NILFS_SS_SYNDT; - ii = sci->sc_dsync_inode; - if (!test_bit(NILFS_I_BUSY, &ii->i_state)) - break; - - err = nilfs_segctor_scan_file_dsync(sci, &ii->vfs_inode); - if (unlikely(err)) - break; - sci->sc_curseg->sb_sum.flags |= NILFS_SS_LOGEND; - sci->sc_stage.scnt = NILFS_ST_DONE; - return 0; - case NILFS_ST_DONE: - return 0; - default: - BUG(); - } - - break_or_fail: - return err; -} - -/** - * nilfs_segctor_begin_construction - setup segment buffer to make a new log - * @sci: nilfs_sc_info - * @nilfs: nilfs object - */ -static int nilfs_segctor_begin_construction(struct nilfs_sc_info *sci, - struct the_nilfs *nilfs) -{ - struct nilfs_segment_buffer *segbuf, *prev; - __u64 nextnum; - int err, alloc = 0; - - segbuf = nilfs_segbuf_new(sci->sc_super); - if (unlikely(!segbuf)) - return -ENOMEM; - - if (list_empty(&sci->sc_write_logs)) { - nilfs_segbuf_map(segbuf, nilfs->ns_segnum, - nilfs->ns_pseg_offset, nilfs); - if (segbuf->sb_rest_blocks < NILFS_PSEG_MIN_BLOCKS) { - nilfs_shift_to_next_segment(nilfs); - nilfs_segbuf_map(segbuf, nilfs->ns_segnum, 0, nilfs); - } - - segbuf->sb_sum.seg_seq = nilfs->ns_seg_seq; - nextnum = nilfs->ns_nextnum; - - if (nilfs->ns_segnum == nilfs->ns_nextnum) - /* Start from the head of a new full segment */ - alloc++; - } else { - /* Continue logs */ - prev = NILFS_LAST_SEGBUF(&sci->sc_write_logs); - nilfs_segbuf_map_cont(segbuf, prev); - segbuf->sb_sum.seg_seq = prev->sb_sum.seg_seq; - nextnum = prev->sb_nextnum; - - if (segbuf->sb_rest_blocks < NILFS_PSEG_MIN_BLOCKS) { - nilfs_segbuf_map(segbuf, prev->sb_nextnum, 0, nilfs); - segbuf->sb_sum.seg_seq++; - alloc++; - } - } - - err = nilfs_sufile_mark_dirty(nilfs->ns_sufile, segbuf->sb_segnum); - if (err) - goto failed; - - if (alloc) { - err = nilfs_sufile_alloc(nilfs->ns_sufile, &nextnum); - if (err) - goto failed; - } - nilfs_segbuf_set_next_segnum(segbuf, nextnum, nilfs); - - BUG_ON(!list_empty(&sci->sc_segbufs)); - list_add_tail(&segbuf->sb_list, &sci->sc_segbufs); - sci->sc_segbuf_nblocks = segbuf->sb_rest_blocks; - return 0; - - failed: - nilfs_segbuf_free(segbuf); - return err; -} - -static int nilfs_segctor_extend_segments(struct nilfs_sc_info *sci, - struct the_nilfs *nilfs, int nadd) -{ - struct nilfs_segment_buffer *segbuf, *prev; - struct inode *sufile = nilfs->ns_sufile; - __u64 nextnextnum; - LIST_HEAD(list); - int err, ret, i; - - prev = NILFS_LAST_SEGBUF(&sci->sc_segbufs); - /* - * Since the segment specified with nextnum might be allocated during - * the previous construction, the buffer including its segusage may - * not be dirty. The following call ensures that the buffer is dirty - * and will pin the buffer on memory until the sufile is written. - */ - err = nilfs_sufile_mark_dirty(sufile, prev->sb_nextnum); - if (unlikely(err)) - return err; - - for (i = 0; i < nadd; i++) { - /* extend segment info */ - err = -ENOMEM; - segbuf = nilfs_segbuf_new(sci->sc_super); - if (unlikely(!segbuf)) - goto failed; - - /* map this buffer to region of segment on-disk */ - nilfs_segbuf_map(segbuf, prev->sb_nextnum, 0, nilfs); - sci->sc_segbuf_nblocks += segbuf->sb_rest_blocks; - - /* allocate the next next full segment */ - err = nilfs_sufile_alloc(sufile, &nextnextnum); - if (unlikely(err)) - goto failed_segbuf; - - segbuf->sb_sum.seg_seq = prev->sb_sum.seg_seq + 1; - nilfs_segbuf_set_next_segnum(segbuf, nextnextnum, nilfs); - - list_add_tail(&segbuf->sb_list, &list); - prev = segbuf; - } - list_splice_tail(&list, &sci->sc_segbufs); - return 0; - - failed_segbuf: - nilfs_segbuf_free(segbuf); - failed: - list_for_each_entry(segbuf, &list, sb_list) { - ret = nilfs_sufile_free(sufile, segbuf->sb_nextnum); - WARN_ON(ret); /* never fails */ - } - nilfs_destroy_logs(&list); - return err; -} - -static void nilfs_free_incomplete_logs(struct list_head *logs, - struct the_nilfs *nilfs) -{ - struct nilfs_segment_buffer *segbuf, *prev; - struct inode *sufile = nilfs->ns_sufile; - int ret; - - segbuf = NILFS_FIRST_SEGBUF(logs); - if (nilfs->ns_nextnum != segbuf->sb_nextnum) { - ret = nilfs_sufile_free(sufile, segbuf->sb_nextnum); - WARN_ON(ret); /* never fails */ - } - if (atomic_read(&segbuf->sb_err)) { - /* Case 1: The first segment failed */ - if (segbuf->sb_pseg_start != segbuf->sb_fseg_start) - /* Case 1a: Partial segment appended into an existing - segment */ - nilfs_terminate_segment(nilfs, segbuf->sb_fseg_start, - segbuf->sb_fseg_end); - else /* Case 1b: New full segment */ - set_nilfs_discontinued(nilfs); - } - - prev = segbuf; - list_for_each_entry_continue(segbuf, logs, sb_list) { - if (prev->sb_nextnum != segbuf->sb_nextnum) { - ret = nilfs_sufile_free(sufile, segbuf->sb_nextnum); - WARN_ON(ret); /* never fails */ - } - if (atomic_read(&segbuf->sb_err) && - segbuf->sb_segnum != nilfs->ns_nextnum) - /* Case 2: extended segment (!= next) failed */ - nilfs_sufile_set_error(sufile, segbuf->sb_segnum); - prev = segbuf; - } -} - -static void nilfs_segctor_update_segusage(struct nilfs_sc_info *sci, - struct inode *sufile) -{ - struct nilfs_segment_buffer *segbuf; - unsigned long live_blocks; - int ret; - - list_for_each_entry(segbuf, &sci->sc_segbufs, sb_list) { - live_blocks = segbuf->sb_sum.nblocks + - (segbuf->sb_pseg_start - segbuf->sb_fseg_start); - ret = nilfs_sufile_set_segment_usage(sufile, segbuf->sb_segnum, - live_blocks, - sci->sc_seg_ctime); - WARN_ON(ret); /* always succeed because the segusage is dirty */ - } -} - -static void nilfs_cancel_segusage(struct list_head *logs, struct inode *sufile) -{ - struct nilfs_segment_buffer *segbuf; - int ret; - - segbuf = NILFS_FIRST_SEGBUF(logs); - ret = nilfs_sufile_set_segment_usage(sufile, segbuf->sb_segnum, - segbuf->sb_pseg_start - - segbuf->sb_fseg_start, 0); - WARN_ON(ret); /* always succeed because the segusage is dirty */ - - list_for_each_entry_continue(segbuf, logs, sb_list) { - ret = nilfs_sufile_set_segment_usage(sufile, segbuf->sb_segnum, - 0, 0); - WARN_ON(ret); /* always succeed */ - } -} - -static void nilfs_segctor_truncate_segments(struct nilfs_sc_info *sci, - struct nilfs_segment_buffer *last, - struct inode *sufile) -{ - struct nilfs_segment_buffer *segbuf = last; - int ret; - - list_for_each_entry_continue(segbuf, &sci->sc_segbufs, sb_list) { - sci->sc_segbuf_nblocks -= segbuf->sb_rest_blocks; - ret = nilfs_sufile_free(sufile, segbuf->sb_nextnum); - WARN_ON(ret); - } - nilfs_truncate_logs(&sci->sc_segbufs, last); -} - - -static int nilfs_segctor_collect(struct nilfs_sc_info *sci, - struct the_nilfs *nilfs, int mode) -{ - struct nilfs_cstage prev_stage = sci->sc_stage; - int err, nadd = 1; - - /* Collection retry loop */ - for (;;) { - sci->sc_nblk_this_inc = 0; - sci->sc_curseg = NILFS_FIRST_SEGBUF(&sci->sc_segbufs); - - err = nilfs_segctor_reset_segment_buffer(sci); - if (unlikely(err)) - goto failed; - - err = nilfs_segctor_collect_blocks(sci, mode); - sci->sc_nblk_this_inc += sci->sc_curseg->sb_sum.nblocks; - if (!err) - break; - - if (unlikely(err != -E2BIG)) - goto failed; - - /* The current segment is filled up */ - if (mode != SC_LSEG_SR || sci->sc_stage.scnt < NILFS_ST_CPFILE) - break; - - nilfs_clear_logs(&sci->sc_segbufs); - - err = nilfs_segctor_extend_segments(sci, nilfs, nadd); - if (unlikely(err)) - return err; - - if (sci->sc_stage.flags & NILFS_CF_SUFREED) { - err = nilfs_sufile_cancel_freev(nilfs->ns_sufile, - sci->sc_freesegs, - sci->sc_nfreesegs, - NULL); - WARN_ON(err); /* do not happen */ - } - nadd = min_t(int, nadd << 1, SC_MAX_SEGDELTA); - sci->sc_stage = prev_stage; - } - nilfs_segctor_truncate_segments(sci, sci->sc_curseg, nilfs->ns_sufile); - return 0; - - failed: - return err; -} - -static void nilfs_list_replace_buffer(struct buffer_head *old_bh, - struct buffer_head *new_bh) -{ - BUG_ON(!list_empty(&new_bh->b_assoc_buffers)); - - list_replace_init(&old_bh->b_assoc_buffers, &new_bh->b_assoc_buffers); - /* The caller must release old_bh */ -} - -static int -nilfs_segctor_update_payload_blocknr(struct nilfs_sc_info *sci, - struct nilfs_segment_buffer *segbuf, - int mode) -{ - struct inode *inode = NULL; - sector_t blocknr; - unsigned long nfinfo = segbuf->sb_sum.nfinfo; - unsigned long nblocks = 0, ndatablk = 0; - struct nilfs_sc_operations *sc_op = NULL; - struct nilfs_segsum_pointer ssp; - struct nilfs_finfo *finfo = NULL; - union nilfs_binfo binfo; - struct buffer_head *bh, *bh_org; - ino_t ino = 0; - int err = 0; - - if (!nfinfo) - goto out; - - blocknr = segbuf->sb_pseg_start + segbuf->sb_sum.nsumblk; - ssp.bh = NILFS_SEGBUF_FIRST_BH(&segbuf->sb_segsum_buffers); - ssp.offset = sizeof(struct nilfs_segment_summary); - - list_for_each_entry(bh, &segbuf->sb_payload_buffers, b_assoc_buffers) { - if (bh == segbuf->sb_super_root) - break; - if (!finfo) { - finfo = nilfs_segctor_map_segsum_entry( - sci, &ssp, sizeof(*finfo)); - ino = le64_to_cpu(finfo->fi_ino); - nblocks = le32_to_cpu(finfo->fi_nblocks); - ndatablk = le32_to_cpu(finfo->fi_ndatablk); - - inode = bh->b_page->mapping->host; - - if (mode == SC_LSEG_DSYNC) - sc_op = &nilfs_sc_dsync_ops; - else if (ino == NILFS_DAT_INO) - sc_op = &nilfs_sc_dat_ops; - else /* file blocks */ - sc_op = &nilfs_sc_file_ops; - } - bh_org = bh; - get_bh(bh_org); - err = nilfs_bmap_assign(NILFS_I(inode)->i_bmap, &bh, blocknr, - &binfo); - if (bh != bh_org) - nilfs_list_replace_buffer(bh_org, bh); - brelse(bh_org); - if (unlikely(err)) - goto failed_bmap; - - if (ndatablk > 0) - sc_op->write_data_binfo(sci, &ssp, &binfo); - else - sc_op->write_node_binfo(sci, &ssp, &binfo); - - blocknr++; - if (--nblocks == 0) { - finfo = NULL; - if (--nfinfo == 0) - break; - } else if (ndatablk > 0) - ndatablk--; - } - out: - return 0; - - failed_bmap: - return err; -} - -static int nilfs_segctor_assign(struct nilfs_sc_info *sci, int mode) -{ - struct nilfs_segment_buffer *segbuf; - int err; - - list_for_each_entry(segbuf, &sci->sc_segbufs, sb_list) { - err = nilfs_segctor_update_payload_blocknr(sci, segbuf, mode); - if (unlikely(err)) - return err; - nilfs_segbuf_fill_in_segsum(segbuf); - } - return 0; -} - -static void nilfs_begin_page_io(struct page *page) -{ - if (!page || PageWriteback(page)) - /* For split b-tree node pages, this function may be called - twice. We ignore the 2nd or later calls by this check. */ - return; - - lock_page(page); - clear_page_dirty_for_io(page); - set_page_writeback(page); - unlock_page(page); -} - -static void nilfs_segctor_prepare_write(struct nilfs_sc_info *sci) -{ - struct nilfs_segment_buffer *segbuf; - struct page *bd_page = NULL, *fs_page = NULL; - - list_for_each_entry(segbuf, &sci->sc_segbufs, sb_list) { - struct buffer_head *bh; - - list_for_each_entry(bh, &segbuf->sb_segsum_buffers, - b_assoc_buffers) { - if (bh->b_page != bd_page) { - if (bd_page) { - lock_page(bd_page); - clear_page_dirty_for_io(bd_page); - set_page_writeback(bd_page); - unlock_page(bd_page); - } - bd_page = bh->b_page; - } - } - - list_for_each_entry(bh, &segbuf->sb_payload_buffers, - b_assoc_buffers) { - if (bh == segbuf->sb_super_root) { - if (bh->b_page != bd_page) { - lock_page(bd_page); - clear_page_dirty_for_io(bd_page); - set_page_writeback(bd_page); - unlock_page(bd_page); - bd_page = bh->b_page; - } - break; - } - if (bh->b_page != fs_page) { - nilfs_begin_page_io(fs_page); - fs_page = bh->b_page; - } - } - } - if (bd_page) { - lock_page(bd_page); - clear_page_dirty_for_io(bd_page); - set_page_writeback(bd_page); - unlock_page(bd_page); - } - nilfs_begin_page_io(fs_page); -} - -static int nilfs_segctor_write(struct nilfs_sc_info *sci, - struct the_nilfs *nilfs) -{ - int ret; - - ret = nilfs_write_logs(&sci->sc_segbufs, nilfs); - list_splice_tail_init(&sci->sc_segbufs, &sci->sc_write_logs); - return ret; -} - -static void nilfs_end_page_io(struct page *page, int err) -{ - if (!page) - return; - - if (buffer_nilfs_node(page_buffers(page)) && !PageWriteback(page)) { - /* - * For b-tree node pages, this function may be called twice - * or more because they might be split in a segment. - */ - if (PageDirty(page)) { - /* - * For pages holding split b-tree node buffers, dirty - * flag on the buffers may be cleared discretely. - * In that case, the page is once redirtied for - * remaining buffers, and it must be cancelled if - * all the buffers get cleaned later. - */ - lock_page(page); - if (nilfs_page_buffers_clean(page)) - __nilfs_clear_page_dirty(page); - unlock_page(page); - } - return; - } - - if (!err) { - if (!nilfs_page_buffers_clean(page)) - __set_page_dirty_nobuffers(page); - ClearPageError(page); - } else { - __set_page_dirty_nobuffers(page); - SetPageError(page); - } - - end_page_writeback(page); -} - -static void nilfs_abort_logs(struct list_head *logs, int err) -{ - struct nilfs_segment_buffer *segbuf; - struct page *bd_page = NULL, *fs_page = NULL; - struct buffer_head *bh; - - if (list_empty(logs)) - return; - - list_for_each_entry(segbuf, logs, sb_list) { - list_for_each_entry(bh, &segbuf->sb_segsum_buffers, - b_assoc_buffers) { - if (bh->b_page != bd_page) { - if (bd_page) - end_page_writeback(bd_page); - bd_page = bh->b_page; - } - } - - list_for_each_entry(bh, &segbuf->sb_payload_buffers, - b_assoc_buffers) { - if (bh == segbuf->sb_super_root) { - if (bh->b_page != bd_page) { - end_page_writeback(bd_page); - bd_page = bh->b_page; - } - break; - } - if (bh->b_page != fs_page) { - nilfs_end_page_io(fs_page, err); - fs_page = bh->b_page; - } - } - } - if (bd_page) - end_page_writeback(bd_page); - - nilfs_end_page_io(fs_page, err); -} - -static void nilfs_segctor_abort_construction(struct nilfs_sc_info *sci, - struct the_nilfs *nilfs, int err) -{ - LIST_HEAD(logs); - int ret; - - list_splice_tail_init(&sci->sc_write_logs, &logs); - ret = nilfs_wait_on_logs(&logs); - nilfs_abort_logs(&logs, ret ? : err); - - list_splice_tail_init(&sci->sc_segbufs, &logs); - nilfs_cancel_segusage(&logs, nilfs->ns_sufile); - nilfs_free_incomplete_logs(&logs, nilfs); - - if (sci->sc_stage.flags & NILFS_CF_SUFREED) { - ret = nilfs_sufile_cancel_freev(nilfs->ns_sufile, - sci->sc_freesegs, - sci->sc_nfreesegs, - NULL); - WARN_ON(ret); /* do not happen */ - } - - nilfs_destroy_logs(&logs); -} - -static void nilfs_set_next_segment(struct the_nilfs *nilfs, - struct nilfs_segment_buffer *segbuf) -{ - nilfs->ns_segnum = segbuf->sb_segnum; - nilfs->ns_nextnum = segbuf->sb_nextnum; - nilfs->ns_pseg_offset = segbuf->sb_pseg_start - segbuf->sb_fseg_start - + segbuf->sb_sum.nblocks; - nilfs->ns_seg_seq = segbuf->sb_sum.seg_seq; - nilfs->ns_ctime = segbuf->sb_sum.ctime; -} - -static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci) -{ - struct nilfs_segment_buffer *segbuf; - struct page *bd_page = NULL, *fs_page = NULL; - struct the_nilfs *nilfs = sci->sc_super->s_fs_info; - int update_sr = false; - - list_for_each_entry(segbuf, &sci->sc_write_logs, sb_list) { - struct buffer_head *bh; - - list_for_each_entry(bh, &segbuf->sb_segsum_buffers, - b_assoc_buffers) { - set_buffer_uptodate(bh); - clear_buffer_dirty(bh); - if (bh->b_page != bd_page) { - if (bd_page) - end_page_writeback(bd_page); - bd_page = bh->b_page; - } - } - /* - * We assume that the buffers which belong to the same page - * continue over the buffer list. - * Under this assumption, the last BHs of pages is - * identifiable by the discontinuity of bh->b_page - * (page != fs_page). - * - * For B-tree node blocks, however, this assumption is not - * guaranteed. The cleanup code of B-tree node pages needs - * special care. - */ - list_for_each_entry(bh, &segbuf->sb_payload_buffers, - b_assoc_buffers) { - set_buffer_uptodate(bh); - clear_buffer_dirty(bh); - clear_buffer_delay(bh); - clear_buffer_nilfs_volatile(bh); - clear_buffer_nilfs_redirected(bh); - if (bh == segbuf->sb_super_root) { - if (bh->b_page != bd_page) { - end_page_writeback(bd_page); - bd_page = bh->b_page; - } - update_sr = true; - break; - } - if (bh->b_page != fs_page) { - nilfs_end_page_io(fs_page, 0); - fs_page = bh->b_page; - } - } - - if (!nilfs_segbuf_simplex(segbuf)) { - if (segbuf->sb_sum.flags & NILFS_SS_LOGBGN) { - set_bit(NILFS_SC_UNCLOSED, &sci->sc_flags); - sci->sc_lseg_stime = jiffies; - } - if (segbuf->sb_sum.flags & NILFS_SS_LOGEND) - clear_bit(NILFS_SC_UNCLOSED, &sci->sc_flags); - } - } - /* - * Since pages may continue over multiple segment buffers, - * end of the last page must be checked outside of the loop. - */ - if (bd_page) - end_page_writeback(bd_page); - - nilfs_end_page_io(fs_page, 0); - - nilfs_drop_collected_inodes(&sci->sc_dirty_files); - - if (nilfs_doing_gc()) - nilfs_drop_collected_inodes(&sci->sc_gc_inodes); - else - nilfs->ns_nongc_ctime = sci->sc_seg_ctime; - - sci->sc_nblk_inc += sci->sc_nblk_this_inc; - - segbuf = NILFS_LAST_SEGBUF(&sci->sc_write_logs); - nilfs_set_next_segment(nilfs, segbuf); - - if (update_sr) { - nilfs_set_last_segment(nilfs, segbuf->sb_pseg_start, - segbuf->sb_sum.seg_seq, nilfs->ns_cno++); - - clear_bit(NILFS_SC_HAVE_DELTA, &sci->sc_flags); - clear_bit(NILFS_SC_DIRTY, &sci->sc_flags); - set_bit(NILFS_SC_SUPER_ROOT, &sci->sc_flags); - nilfs_segctor_clear_metadata_dirty(sci); - } else - clear_bit(NILFS_SC_SUPER_ROOT, &sci->sc_flags); -} - -static int nilfs_segctor_wait(struct nilfs_sc_info *sci) -{ - int ret; - - ret = nilfs_wait_on_logs(&sci->sc_write_logs); - if (!ret) { - nilfs_segctor_complete_write(sci); - nilfs_destroy_logs(&sci->sc_write_logs); - } - return ret; -} - -static int nilfs_segctor_collect_dirty_files(struct nilfs_sc_info *sci, - struct the_nilfs *nilfs) -{ - struct nilfs_inode_info *ii, *n; - struct inode *ifile = sci->sc_root->ifile; - - spin_lock(&nilfs->ns_inode_lock); - retry: - list_for_each_entry_safe(ii, n, &nilfs->ns_dirty_files, i_dirty) { - if (!ii->i_bh) { - struct buffer_head *ibh; - int err; - - spin_unlock(&nilfs->ns_inode_lock); - err = nilfs_ifile_get_inode_block( - ifile, ii->vfs_inode.i_ino, &ibh); - if (unlikely(err)) { - nilfs_warning(sci->sc_super, __func__, - "failed to get inode block.\n"); - return err; - } - mark_buffer_dirty(ibh); - nilfs_mdt_mark_dirty(ifile); - spin_lock(&nilfs->ns_inode_lock); - if (likely(!ii->i_bh)) - ii->i_bh = ibh; - else - brelse(ibh); - goto retry; - } - - clear_bit(NILFS_I_QUEUED, &ii->i_state); - set_bit(NILFS_I_BUSY, &ii->i_state); - list_move_tail(&ii->i_dirty, &sci->sc_dirty_files); - } - spin_unlock(&nilfs->ns_inode_lock); - - return 0; -} - -static void nilfs_segctor_drop_written_files(struct nilfs_sc_info *sci, - struct the_nilfs *nilfs) -{ - struct nilfs_transaction_info *ti = current->journal_info; - struct nilfs_inode_info *ii, *n; - - spin_lock(&nilfs->ns_inode_lock); - list_for_each_entry_safe(ii, n, &sci->sc_dirty_files, i_dirty) { - if (!test_and_clear_bit(NILFS_I_UPDATED, &ii->i_state) || - test_bit(NILFS_I_DIRTY, &ii->i_state)) - continue; - - clear_bit(NILFS_I_BUSY, &ii->i_state); - brelse(ii->i_bh); - ii->i_bh = NULL; - list_move_tail(&ii->i_dirty, &ti->ti_garbage); - } - spin_unlock(&nilfs->ns_inode_lock); -} - -/* - * Main procedure of segment constructor - */ -static int nilfs_segctor_do_construct(struct nilfs_sc_info *sci, int mode) -{ - struct the_nilfs *nilfs = sci->sc_super->s_fs_info; - int err; - - sci->sc_stage.scnt = NILFS_ST_INIT; - sci->sc_cno = nilfs->ns_cno; - - err = nilfs_segctor_collect_dirty_files(sci, nilfs); - if (unlikely(err)) - goto out; - - if (nilfs_test_metadata_dirty(nilfs, sci->sc_root)) - set_bit(NILFS_SC_DIRTY, &sci->sc_flags); - - if (nilfs_segctor_clean(sci)) - goto out; - - do { - sci->sc_stage.flags &= ~NILFS_CF_HISTORY_MASK; - - err = nilfs_segctor_begin_construction(sci, nilfs); - if (unlikely(err)) - goto out; - - /* Update time stamp */ - sci->sc_seg_ctime = get_seconds(); - - err = nilfs_segctor_collect(sci, nilfs, mode); - if (unlikely(err)) - goto failed; - - /* Avoid empty segment */ - if (sci->sc_stage.scnt == NILFS_ST_DONE && - nilfs_segbuf_empty(sci->sc_curseg)) { - nilfs_segctor_abort_construction(sci, nilfs, 1); - goto out; - } - - err = nilfs_segctor_assign(sci, mode); - if (unlikely(err)) - goto failed; - - if (sci->sc_stage.flags & NILFS_CF_IFILE_STARTED) - nilfs_segctor_fill_in_file_bmap(sci); - - if (mode == SC_LSEG_SR && - sci->sc_stage.scnt >= NILFS_ST_CPFILE) { - err = nilfs_segctor_fill_in_checkpoint(sci); - if (unlikely(err)) - goto failed_to_write; - - nilfs_segctor_fill_in_super_root(sci, nilfs); - } - nilfs_segctor_update_segusage(sci, nilfs->ns_sufile); - - /* Write partial segments */ - nilfs_segctor_prepare_write(sci); - - nilfs_add_checksums_on_logs(&sci->sc_segbufs, - nilfs->ns_crc_seed); - - err = nilfs_segctor_write(sci, nilfs); - if (unlikely(err)) - goto failed_to_write; - - if (sci->sc_stage.scnt == NILFS_ST_DONE || - nilfs->ns_blocksize_bits != PAGE_CACHE_SHIFT) { - /* - * At this point, we avoid double buffering - * for blocksize < pagesize because page dirty - * flag is turned off during write and dirty - * buffers are not properly collected for - * pages crossing over segments. - */ - err = nilfs_segctor_wait(sci); - if (err) - goto failed_to_write; - } - } while (sci->sc_stage.scnt != NILFS_ST_DONE); - - out: - nilfs_segctor_drop_written_files(sci, nilfs); - return err; - - failed_to_write: - if (sci->sc_stage.flags & NILFS_CF_IFILE_STARTED) - nilfs_redirty_inodes(&sci->sc_dirty_files); - - failed: - if (nilfs_doing_gc()) - nilfs_redirty_inodes(&sci->sc_gc_inodes); - nilfs_segctor_abort_construction(sci, nilfs, err); - goto out; -} - -/** - * nilfs_segctor_start_timer - set timer of background write - * @sci: nilfs_sc_info - * - * If the timer has already been set, it ignores the new request. - * This function MUST be called within a section locking the segment - * semaphore. - */ -static void nilfs_segctor_start_timer(struct nilfs_sc_info *sci) -{ - spin_lock(&sci->sc_state_lock); - if (!(sci->sc_state & NILFS_SEGCTOR_COMMIT)) { - sci->sc_timer.expires = jiffies + sci->sc_interval; - add_timer(&sci->sc_timer); - sci->sc_state |= NILFS_SEGCTOR_COMMIT; - } - spin_unlock(&sci->sc_state_lock); -} - -static void nilfs_segctor_do_flush(struct nilfs_sc_info *sci, int bn) -{ - spin_lock(&sci->sc_state_lock); - if (!(sci->sc_flush_request & (1 << bn))) { - unsigned long prev_req = sci->sc_flush_request; - - sci->sc_flush_request |= (1 << bn); - if (!prev_req) - wake_up(&sci->sc_wait_daemon); - } - spin_unlock(&sci->sc_state_lock); -} - -/** - * nilfs_flush_segment - trigger a segment construction for resource control - * @sb: super block - * @ino: inode number of the file to be flushed out. - */ -void nilfs_flush_segment(struct super_block *sb, ino_t ino) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct nilfs_sc_info *sci = nilfs->ns_writer; - - if (!sci || nilfs_doing_construction()) - return; - nilfs_segctor_do_flush(sci, NILFS_MDT_INODE(sb, ino) ? ino : 0); - /* assign bit 0 to data files */ -} - -struct nilfs_segctor_wait_request { - wait_queue_t wq; - __u32 seq; - int err; - atomic_t done; -}; - -static int nilfs_segctor_sync(struct nilfs_sc_info *sci) -{ - struct nilfs_segctor_wait_request wait_req; - int err = 0; - - spin_lock(&sci->sc_state_lock); - init_wait(&wait_req.wq); - wait_req.err = 0; - atomic_set(&wait_req.done, 0); - wait_req.seq = ++sci->sc_seq_request; - spin_unlock(&sci->sc_state_lock); - - init_waitqueue_entry(&wait_req.wq, current); - add_wait_queue(&sci->sc_wait_request, &wait_req.wq); - set_current_state(TASK_INTERRUPTIBLE); - wake_up(&sci->sc_wait_daemon); - - for (;;) { - if (atomic_read(&wait_req.done)) { - err = wait_req.err; - break; - } - if (!signal_pending(current)) { - schedule(); - continue; - } - err = -ERESTARTSYS; - break; - } - finish_wait(&sci->sc_wait_request, &wait_req.wq); - return err; -} - -static void nilfs_segctor_wakeup(struct nilfs_sc_info *sci, int err) -{ - struct nilfs_segctor_wait_request *wrq, *n; - unsigned long flags; - - spin_lock_irqsave(&sci->sc_wait_request.lock, flags); - list_for_each_entry_safe(wrq, n, &sci->sc_wait_request.task_list, - wq.task_list) { - if (!atomic_read(&wrq->done) && - nilfs_cnt32_ge(sci->sc_seq_done, wrq->seq)) { - wrq->err = err; - atomic_set(&wrq->done, 1); - } - if (atomic_read(&wrq->done)) { - wrq->wq.func(&wrq->wq, - TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, - 0, NULL); - } - } - spin_unlock_irqrestore(&sci->sc_wait_request.lock, flags); -} - -/** - * nilfs_construct_segment - construct a logical segment - * @sb: super block - * - * Return Value: On success, 0 is retured. On errors, one of the following - * negative error code is returned. - * - * %-EROFS - Read only filesystem. - * - * %-EIO - I/O error - * - * %-ENOSPC - No space left on device (only in a panic state). - * - * %-ERESTARTSYS - Interrupted. - * - * %-ENOMEM - Insufficient memory available. - */ -int nilfs_construct_segment(struct super_block *sb) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct nilfs_sc_info *sci = nilfs->ns_writer; - struct nilfs_transaction_info *ti; - int err; - - if (!sci) - return -EROFS; - - /* A call inside transactions causes a deadlock. */ - BUG_ON((ti = current->journal_info) && ti->ti_magic == NILFS_TI_MAGIC); - - err = nilfs_segctor_sync(sci); - return err; -} - -/** - * nilfs_construct_dsync_segment - construct a data-only logical segment - * @sb: super block - * @inode: inode whose data blocks should be written out - * @start: start byte offset - * @end: end byte offset (inclusive) - * - * Return Value: On success, 0 is retured. On errors, one of the following - * negative error code is returned. - * - * %-EROFS - Read only filesystem. - * - * %-EIO - I/O error - * - * %-ENOSPC - No space left on device (only in a panic state). - * - * %-ERESTARTSYS - Interrupted. - * - * %-ENOMEM - Insufficient memory available. - */ -int nilfs_construct_dsync_segment(struct super_block *sb, struct inode *inode, - loff_t start, loff_t end) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct nilfs_sc_info *sci = nilfs->ns_writer; - struct nilfs_inode_info *ii; - struct nilfs_transaction_info ti; - int err = 0; - - if (!sci) - return -EROFS; - - nilfs_transaction_lock(sb, &ti, 0); - - ii = NILFS_I(inode); - if (test_bit(NILFS_I_INODE_DIRTY, &ii->i_state) || - nilfs_test_opt(nilfs, STRICT_ORDER) || - test_bit(NILFS_SC_UNCLOSED, &sci->sc_flags) || - nilfs_discontinued(nilfs)) { - nilfs_transaction_unlock(sb); - err = nilfs_segctor_sync(sci); - return err; - } - - spin_lock(&nilfs->ns_inode_lock); - if (!test_bit(NILFS_I_QUEUED, &ii->i_state) && - !test_bit(NILFS_I_BUSY, &ii->i_state)) { - spin_unlock(&nilfs->ns_inode_lock); - nilfs_transaction_unlock(sb); - return 0; - } - spin_unlock(&nilfs->ns_inode_lock); - sci->sc_dsync_inode = ii; - sci->sc_dsync_start = start; - sci->sc_dsync_end = end; - - err = nilfs_segctor_do_construct(sci, SC_LSEG_DSYNC); - - nilfs_transaction_unlock(sb); - return err; -} - -#define FLUSH_FILE_BIT (0x1) /* data file only */ -#define FLUSH_DAT_BIT (1 << NILFS_DAT_INO) /* DAT only */ - -/** - * nilfs_segctor_accept - record accepted sequence count of log-write requests - * @sci: segment constructor object - */ -static void nilfs_segctor_accept(struct nilfs_sc_info *sci) -{ - spin_lock(&sci->sc_state_lock); - sci->sc_seq_accepted = sci->sc_seq_request; - spin_unlock(&sci->sc_state_lock); - del_timer_sync(&sci->sc_timer); -} - -/** - * nilfs_segctor_notify - notify the result of request to caller threads - * @sci: segment constructor object - * @mode: mode of log forming - * @err: error code to be notified - */ -static void nilfs_segctor_notify(struct nilfs_sc_info *sci, int mode, int err) -{ - /* Clear requests (even when the construction failed) */ - spin_lock(&sci->sc_state_lock); - - if (mode == SC_LSEG_SR) { - sci->sc_state &= ~NILFS_SEGCTOR_COMMIT; - sci->sc_seq_done = sci->sc_seq_accepted; - nilfs_segctor_wakeup(sci, err); - sci->sc_flush_request = 0; - } else { - if (mode == SC_FLUSH_FILE) - sci->sc_flush_request &= ~FLUSH_FILE_BIT; - else if (mode == SC_FLUSH_DAT) - sci->sc_flush_request &= ~FLUSH_DAT_BIT; - - /* re-enable timer if checkpoint creation was not done */ - if ((sci->sc_state & NILFS_SEGCTOR_COMMIT) && - time_before(jiffies, sci->sc_timer.expires)) - add_timer(&sci->sc_timer); - } - spin_unlock(&sci->sc_state_lock); -} - -/** - * nilfs_segctor_construct - form logs and write them to disk - * @sci: segment constructor object - * @mode: mode of log forming - */ -static int nilfs_segctor_construct(struct nilfs_sc_info *sci, int mode) -{ - struct the_nilfs *nilfs = sci->sc_super->s_fs_info; - struct nilfs_super_block **sbp; - int err = 0; - - nilfs_segctor_accept(sci); - - if (nilfs_discontinued(nilfs)) - mode = SC_LSEG_SR; - if (!nilfs_segctor_confirm(sci)) - err = nilfs_segctor_do_construct(sci, mode); - - if (likely(!err)) { - if (mode != SC_FLUSH_DAT) - atomic_set(&nilfs->ns_ndirtyblks, 0); - if (test_bit(NILFS_SC_SUPER_ROOT, &sci->sc_flags) && - nilfs_discontinued(nilfs)) { - down_write(&nilfs->ns_sem); - err = -EIO; - sbp = nilfs_prepare_super(sci->sc_super, - nilfs_sb_will_flip(nilfs)); - if (likely(sbp)) { - nilfs_set_log_cursor(sbp[0], nilfs); - err = nilfs_commit_super(sci->sc_super, - NILFS_SB_COMMIT); - } - up_write(&nilfs->ns_sem); - } - } - - nilfs_segctor_notify(sci, mode, err); - return err; -} - -static void nilfs_construction_timeout(unsigned long data) -{ - struct task_struct *p = (struct task_struct *)data; - wake_up_process(p); -} - -static void -nilfs_remove_written_gcinodes(struct the_nilfs *nilfs, struct list_head *head) -{ - struct nilfs_inode_info *ii, *n; - - list_for_each_entry_safe(ii, n, head, i_dirty) { - if (!test_bit(NILFS_I_UPDATED, &ii->i_state)) - continue; - list_del_init(&ii->i_dirty); - truncate_inode_pages(&ii->vfs_inode.i_data, 0); - nilfs_btnode_cache_clear(&ii->i_btnode_cache); - iput(&ii->vfs_inode); - } -} - -int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv, - void **kbufs) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct nilfs_sc_info *sci = nilfs->ns_writer; - struct nilfs_transaction_info ti; - int err; - - if (unlikely(!sci)) - return -EROFS; - - nilfs_transaction_lock(sb, &ti, 1); - - err = nilfs_mdt_save_to_shadow_map(nilfs->ns_dat); - if (unlikely(err)) - goto out_unlock; - - err = nilfs_ioctl_prepare_clean_segments(nilfs, argv, kbufs); - if (unlikely(err)) { - nilfs_mdt_restore_from_shadow_map(nilfs->ns_dat); - goto out_unlock; - } - - sci->sc_freesegs = kbufs[4]; - sci->sc_nfreesegs = argv[4].v_nmembs; - list_splice_tail_init(&nilfs->ns_gc_inodes, &sci->sc_gc_inodes); - - for (;;) { - err = nilfs_segctor_construct(sci, SC_LSEG_SR); - nilfs_remove_written_gcinodes(nilfs, &sci->sc_gc_inodes); - - if (likely(!err)) - break; - - nilfs_warning(sb, __func__, - "segment construction failed. (err=%d)", err); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(sci->sc_interval); - } - if (nilfs_test_opt(nilfs, DISCARD)) { - int ret = nilfs_discard_segments(nilfs, sci->sc_freesegs, - sci->sc_nfreesegs); - if (ret) { - printk(KERN_WARNING - "NILFS warning: error %d on discard request, " - "turning discards off for the device\n", ret); - nilfs_clear_opt(nilfs, DISCARD); - } - } - - out_unlock: - sci->sc_freesegs = NULL; - sci->sc_nfreesegs = 0; - nilfs_mdt_clear_shadow_map(nilfs->ns_dat); - nilfs_transaction_unlock(sb); - return err; -} - -static void nilfs_segctor_thread_construct(struct nilfs_sc_info *sci, int mode) -{ - struct nilfs_transaction_info ti; - - nilfs_transaction_lock(sci->sc_super, &ti, 0); - nilfs_segctor_construct(sci, mode); - - /* - * Unclosed segment should be retried. We do this using sc_timer. - * Timeout of sc_timer will invoke complete construction which leads - * to close the current logical segment. - */ - if (test_bit(NILFS_SC_UNCLOSED, &sci->sc_flags)) - nilfs_segctor_start_timer(sci); - - nilfs_transaction_unlock(sci->sc_super); -} - -static void nilfs_segctor_do_immediate_flush(struct nilfs_sc_info *sci) -{ - int mode = 0; - int err; - - spin_lock(&sci->sc_state_lock); - mode = (sci->sc_flush_request & FLUSH_DAT_BIT) ? - SC_FLUSH_DAT : SC_FLUSH_FILE; - spin_unlock(&sci->sc_state_lock); - - if (mode) { - err = nilfs_segctor_do_construct(sci, mode); - - spin_lock(&sci->sc_state_lock); - sci->sc_flush_request &= (mode == SC_FLUSH_FILE) ? - ~FLUSH_FILE_BIT : ~FLUSH_DAT_BIT; - spin_unlock(&sci->sc_state_lock); - } - clear_bit(NILFS_SC_PRIOR_FLUSH, &sci->sc_flags); -} - -static int nilfs_segctor_flush_mode(struct nilfs_sc_info *sci) -{ - if (!test_bit(NILFS_SC_UNCLOSED, &sci->sc_flags) || - time_before(jiffies, sci->sc_lseg_stime + sci->sc_mjcp_freq)) { - if (!(sci->sc_flush_request & ~FLUSH_FILE_BIT)) - return SC_FLUSH_FILE; - else if (!(sci->sc_flush_request & ~FLUSH_DAT_BIT)) - return SC_FLUSH_DAT; - } - return SC_LSEG_SR; -} - -/** - * nilfs_segctor_thread - main loop of the segment constructor thread. - * @arg: pointer to a struct nilfs_sc_info. - * - * nilfs_segctor_thread() initializes a timer and serves as a daemon - * to execute segment constructions. - */ -static int nilfs_segctor_thread(void *arg) -{ - struct nilfs_sc_info *sci = (struct nilfs_sc_info *)arg; - struct the_nilfs *nilfs = sci->sc_super->s_fs_info; - int timeout = 0; - - sci->sc_timer.data = (unsigned long)current; - sci->sc_timer.function = nilfs_construction_timeout; - - /* start sync. */ - sci->sc_task = current; - wake_up(&sci->sc_wait_task); /* for nilfs_segctor_start_thread() */ - printk(KERN_INFO - "segctord starting. Construction interval = %lu seconds, " - "CP frequency < %lu seconds\n", - sci->sc_interval / HZ, sci->sc_mjcp_freq / HZ); - - spin_lock(&sci->sc_state_lock); - loop: - for (;;) { - int mode; - - if (sci->sc_state & NILFS_SEGCTOR_QUIT) - goto end_thread; - - if (timeout || sci->sc_seq_request != sci->sc_seq_done) - mode = SC_LSEG_SR; - else if (!sci->sc_flush_request) - break; - else - mode = nilfs_segctor_flush_mode(sci); - - spin_unlock(&sci->sc_state_lock); - nilfs_segctor_thread_construct(sci, mode); - spin_lock(&sci->sc_state_lock); - timeout = 0; - } - - - if (freezing(current)) { - spin_unlock(&sci->sc_state_lock); - try_to_freeze(); - spin_lock(&sci->sc_state_lock); - } else { - DEFINE_WAIT(wait); - int should_sleep = 1; - - prepare_to_wait(&sci->sc_wait_daemon, &wait, - TASK_INTERRUPTIBLE); - - if (sci->sc_seq_request != sci->sc_seq_done) - should_sleep = 0; - else if (sci->sc_flush_request) - should_sleep = 0; - else if (sci->sc_state & NILFS_SEGCTOR_COMMIT) - should_sleep = time_before(jiffies, - sci->sc_timer.expires); - - if (should_sleep) { - spin_unlock(&sci->sc_state_lock); - schedule(); - spin_lock(&sci->sc_state_lock); - } - finish_wait(&sci->sc_wait_daemon, &wait); - timeout = ((sci->sc_state & NILFS_SEGCTOR_COMMIT) && - time_after_eq(jiffies, sci->sc_timer.expires)); - - if (nilfs_sb_dirty(nilfs) && nilfs_sb_need_update(nilfs)) - set_nilfs_discontinued(nilfs); - } - goto loop; - - end_thread: - spin_unlock(&sci->sc_state_lock); - - /* end sync. */ - sci->sc_task = NULL; - wake_up(&sci->sc_wait_task); /* for nilfs_segctor_kill_thread() */ - return 0; -} - -static int nilfs_segctor_start_thread(struct nilfs_sc_info *sci) -{ - struct task_struct *t; - - t = kthread_run(nilfs_segctor_thread, sci, "segctord"); - if (IS_ERR(t)) { - int err = PTR_ERR(t); - - printk(KERN_ERR "NILFS: error %d creating segctord thread\n", - err); - return err; - } - wait_event(sci->sc_wait_task, sci->sc_task != NULL); - return 0; -} - -static void nilfs_segctor_kill_thread(struct nilfs_sc_info *sci) - __acquires(&sci->sc_state_lock) - __releases(&sci->sc_state_lock) -{ - sci->sc_state |= NILFS_SEGCTOR_QUIT; - - while (sci->sc_task) { - wake_up(&sci->sc_wait_daemon); - spin_unlock(&sci->sc_state_lock); - wait_event(sci->sc_wait_task, sci->sc_task == NULL); - spin_lock(&sci->sc_state_lock); - } -} - -/* - * Setup & clean-up functions - */ -static struct nilfs_sc_info *nilfs_segctor_new(struct super_block *sb, - struct nilfs_root *root) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct nilfs_sc_info *sci; - - sci = kzalloc(sizeof(*sci), GFP_KERNEL); - if (!sci) - return NULL; - - sci->sc_super = sb; - - nilfs_get_root(root); - sci->sc_root = root; - - init_waitqueue_head(&sci->sc_wait_request); - init_waitqueue_head(&sci->sc_wait_daemon); - init_waitqueue_head(&sci->sc_wait_task); - spin_lock_init(&sci->sc_state_lock); - INIT_LIST_HEAD(&sci->sc_dirty_files); - INIT_LIST_HEAD(&sci->sc_segbufs); - INIT_LIST_HEAD(&sci->sc_write_logs); - INIT_LIST_HEAD(&sci->sc_gc_inodes); - init_timer(&sci->sc_timer); - - sci->sc_interval = HZ * NILFS_SC_DEFAULT_TIMEOUT; - sci->sc_mjcp_freq = HZ * NILFS_SC_DEFAULT_SR_FREQ; - sci->sc_watermark = NILFS_SC_DEFAULT_WATERMARK; - - if (nilfs->ns_interval) - sci->sc_interval = HZ * nilfs->ns_interval; - if (nilfs->ns_watermark) - sci->sc_watermark = nilfs->ns_watermark; - return sci; -} - -static void nilfs_segctor_write_out(struct nilfs_sc_info *sci) -{ - int ret, retrycount = NILFS_SC_CLEANUP_RETRY; - - /* The segctord thread was stopped and its timer was removed. - But some tasks remain. */ - do { - struct nilfs_transaction_info ti; - - nilfs_transaction_lock(sci->sc_super, &ti, 0); - ret = nilfs_segctor_construct(sci, SC_LSEG_SR); - nilfs_transaction_unlock(sci->sc_super); - - } while (ret && retrycount-- > 0); -} - -/** - * nilfs_segctor_destroy - destroy the segment constructor. - * @sci: nilfs_sc_info - * - * nilfs_segctor_destroy() kills the segctord thread and frees - * the nilfs_sc_info struct. - * Caller must hold the segment semaphore. - */ -static void nilfs_segctor_destroy(struct nilfs_sc_info *sci) -{ - struct the_nilfs *nilfs = sci->sc_super->s_fs_info; - int flag; - - up_write(&nilfs->ns_segctor_sem); - - spin_lock(&sci->sc_state_lock); - nilfs_segctor_kill_thread(sci); - flag = ((sci->sc_state & NILFS_SEGCTOR_COMMIT) || sci->sc_flush_request - || sci->sc_seq_request != sci->sc_seq_done); - spin_unlock(&sci->sc_state_lock); - - if (flag || !nilfs_segctor_confirm(sci)) - nilfs_segctor_write_out(sci); - - if (!list_empty(&sci->sc_dirty_files)) { - nilfs_warning(sci->sc_super, __func__, - "dirty file(s) after the final construction\n"); - nilfs_dispose_list(nilfs, &sci->sc_dirty_files, 1); - } - - WARN_ON(!list_empty(&sci->sc_segbufs)); - WARN_ON(!list_empty(&sci->sc_write_logs)); - - nilfs_put_root(sci->sc_root); - - down_write(&nilfs->ns_segctor_sem); - - del_timer_sync(&sci->sc_timer); - kfree(sci); -} - -/** - * nilfs_attach_log_writer - attach log writer - * @sb: super block instance - * @root: root object of the current filesystem tree - * - * This allocates a log writer object, initializes it, and starts the - * log writer. - * - * Return Value: On success, 0 is returned. On error, one of the following - * negative error code is returned. - * - * %-ENOMEM - Insufficient memory available. - */ -int nilfs_attach_log_writer(struct super_block *sb, struct nilfs_root *root) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - int err; - - if (nilfs->ns_writer) { - /* - * This happens if the filesystem was remounted - * read/write after nilfs_error degenerated it into a - * read-only mount. - */ - nilfs_detach_log_writer(sb); - } - - nilfs->ns_writer = nilfs_segctor_new(sb, root); - if (!nilfs->ns_writer) - return -ENOMEM; - - err = nilfs_segctor_start_thread(nilfs->ns_writer); - if (err) { - kfree(nilfs->ns_writer); - nilfs->ns_writer = NULL; - } - return err; -} - -/** - * nilfs_detach_log_writer - destroy log writer - * @sb: super block instance - * - * This kills log writer daemon, frees the log writer object, and - * destroys list of dirty files. - */ -void nilfs_detach_log_writer(struct super_block *sb) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - LIST_HEAD(garbage_list); - - down_write(&nilfs->ns_segctor_sem); - if (nilfs->ns_writer) { - nilfs_segctor_destroy(nilfs->ns_writer); - nilfs->ns_writer = NULL; - } - - /* Force to free the list of dirty files */ - spin_lock(&nilfs->ns_inode_lock); - if (!list_empty(&nilfs->ns_dirty_files)) { - list_splice_init(&nilfs->ns_dirty_files, &garbage_list); - nilfs_warning(sb, __func__, - "Hit dirty file after stopped log writer\n"); - } - spin_unlock(&nilfs->ns_inode_lock); - up_write(&nilfs->ns_segctor_sem); - - nilfs_dispose_list(nilfs, &garbage_list, 1); -} diff --git a/ANDROID_3.4.5/fs/nilfs2/segment.h b/ANDROID_3.4.5/fs/nilfs2/segment.h deleted file mode 100644 index 38a1d001..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/segment.h +++ /dev/null @@ -1,246 +0,0 @@ -/* - * segment.h - NILFS Segment constructor prototypes and definitions - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Ryusuke Konishi <ryusuke@osrg.net> - * - */ -#ifndef _NILFS_SEGMENT_H -#define _NILFS_SEGMENT_H - -#include <linux/types.h> -#include <linux/fs.h> -#include <linux/buffer_head.h> -#include <linux/nilfs2_fs.h> -#include "nilfs.h" - -struct nilfs_root; - -/** - * struct nilfs_recovery_info - Recovery information - * @ri_need_recovery: Recovery status - * @ri_super_root: Block number of the last super root - * @ri_ri_cno: Number of the last checkpoint - * @ri_lsegs_start: Region for roll-forwarding (start block number) - * @ri_lsegs_end: Region for roll-forwarding (end block number) - * @ri_lseg_start_seq: Sequence value of the segment at ri_lsegs_start - * @ri_used_segments: List of segments to be mark active - * @ri_pseg_start: Block number of the last partial segment - * @ri_seq: Sequence number on the last partial segment - * @ri_segnum: Segment number on the last partial segment - * @ri_nextnum: Next segment number on the last partial segment - */ -struct nilfs_recovery_info { - int ri_need_recovery; - sector_t ri_super_root; - __u64 ri_cno; - - sector_t ri_lsegs_start; - sector_t ri_lsegs_end; - u64 ri_lsegs_start_seq; - struct list_head ri_used_segments; - sector_t ri_pseg_start; - u64 ri_seq; - __u64 ri_segnum; - __u64 ri_nextnum; -}; - -/* ri_need_recovery */ -#define NILFS_RECOVERY_SR_UPDATED 1 /* The super root was updated */ -#define NILFS_RECOVERY_ROLLFORWARD_DONE 2 /* Rollforward was carried out */ - -/** - * struct nilfs_cstage - Context of collection stage - * @scnt: Stage count - * @flags: State flags - * @dirty_file_ptr: Pointer on dirty_files list, or inode of a target file - * @gc_inode_ptr: Pointer on the list of gc-inodes - */ -struct nilfs_cstage { - int scnt; - unsigned flags; - struct nilfs_inode_info *dirty_file_ptr; - struct nilfs_inode_info *gc_inode_ptr; -}; - -struct nilfs_segment_buffer; - -struct nilfs_segsum_pointer { - struct buffer_head *bh; - unsigned offset; /* offset in bytes */ -}; - -/** - * struct nilfs_sc_info - Segment constructor information - * @sc_super: Back pointer to super_block struct - * @sc_root: root object of the current filesystem tree - * @sc_nblk_inc: Block count of current generation - * @sc_dirty_files: List of files to be written - * @sc_gc_inodes: List of GC inodes having blocks to be written - * @sc_freesegs: array of segment numbers to be freed - * @sc_nfreesegs: number of segments on @sc_freesegs - * @sc_dsync_inode: inode whose data pages are written for a sync operation - * @sc_dsync_start: start byte offset of data pages - * @sc_dsync_end: end byte offset of data pages (inclusive) - * @sc_segbufs: List of segment buffers - * @sc_write_logs: List of segment buffers to hold logs under writing - * @sc_segbuf_nblocks: Number of available blocks in segment buffers. - * @sc_curseg: Current segment buffer - * @sc_stage: Collection stage - * @sc_finfo_ptr: pointer to the current finfo struct in the segment summary - * @sc_binfo_ptr: pointer to the current binfo struct in the segment summary - * @sc_blk_cnt: Block count of a file - * @sc_datablk_cnt: Data block count of a file - * @sc_nblk_this_inc: Number of blocks included in the current logical segment - * @sc_seg_ctime: Creation time - * @sc_cno: checkpoint number of current log - * @sc_flags: Internal flags - * @sc_state_lock: spinlock for sc_state and so on - * @sc_state: Segctord state flags - * @sc_flush_request: inode bitmap of metadata files to be flushed - * @sc_wait_request: Client request queue - * @sc_wait_daemon: Daemon wait queue - * @sc_wait_task: Start/end wait queue to control segctord task - * @sc_seq_request: Request counter - * @sc_seq_accept: Accepted request count - * @sc_seq_done: Completion counter - * @sc_sync: Request of explicit sync operation - * @sc_interval: Timeout value of background construction - * @sc_mjcp_freq: Frequency of creating checkpoints - * @sc_lseg_stime: Start time of the latest logical segment - * @sc_watermark: Watermark for the number of dirty buffers - * @sc_timer: Timer for segctord - * @sc_task: current thread of segctord - */ -struct nilfs_sc_info { - struct super_block *sc_super; - struct nilfs_root *sc_root; - - unsigned long sc_nblk_inc; - - struct list_head sc_dirty_files; - struct list_head sc_gc_inodes; - - __u64 *sc_freesegs; - size_t sc_nfreesegs; - - struct nilfs_inode_info *sc_dsync_inode; - loff_t sc_dsync_start; - loff_t sc_dsync_end; - - /* Segment buffers */ - struct list_head sc_segbufs; - struct list_head sc_write_logs; - unsigned long sc_segbuf_nblocks; - struct nilfs_segment_buffer *sc_curseg; - - struct nilfs_cstage sc_stage; - - struct nilfs_segsum_pointer sc_finfo_ptr; - struct nilfs_segsum_pointer sc_binfo_ptr; - unsigned long sc_blk_cnt; - unsigned long sc_datablk_cnt; - unsigned long sc_nblk_this_inc; - time_t sc_seg_ctime; - __u64 sc_cno; - unsigned long sc_flags; - - spinlock_t sc_state_lock; - unsigned long sc_state; - unsigned long sc_flush_request; - - wait_queue_head_t sc_wait_request; - wait_queue_head_t sc_wait_daemon; - wait_queue_head_t sc_wait_task; - - __u32 sc_seq_request; - __u32 sc_seq_accepted; - __u32 sc_seq_done; - - int sc_sync; - unsigned long sc_interval; - unsigned long sc_mjcp_freq; - unsigned long sc_lseg_stime; /* in 1/HZ seconds */ - unsigned long sc_watermark; - - struct timer_list sc_timer; - struct task_struct *sc_task; -}; - -/* sc_flags */ -enum { - NILFS_SC_DIRTY, /* One or more dirty meta-data blocks exist */ - NILFS_SC_UNCLOSED, /* Logical segment is not closed */ - NILFS_SC_SUPER_ROOT, /* The latest segment has a super root */ - NILFS_SC_PRIOR_FLUSH, /* Requesting immediate flush without making a - checkpoint */ - NILFS_SC_HAVE_DELTA, /* Next checkpoint will have update of files - other than DAT, cpfile, sufile, or files - moved by GC */ -}; - -/* sc_state */ -#define NILFS_SEGCTOR_QUIT 0x0001 /* segctord is being destroyed */ -#define NILFS_SEGCTOR_COMMIT 0x0004 /* committed transaction exists */ - -/* - * Constant parameters - */ -#define NILFS_SC_CLEANUP_RETRY 3 /* Retry count of construction when - destroying segctord */ - -/* - * Default values of timeout, in seconds. - */ -#define NILFS_SC_DEFAULT_TIMEOUT 5 /* Timeout value of dirty blocks. - It triggers construction of a - logical segment with a super root */ -#define NILFS_SC_DEFAULT_SR_FREQ 30 /* Maximum frequency of super root - creation */ - -/* - * The default threshold amount of data, in block counts. - */ -#define NILFS_SC_DEFAULT_WATERMARK 3600 - -/* super.c */ -extern struct kmem_cache *nilfs_transaction_cachep; - -/* segment.c */ -extern void nilfs_relax_pressure_in_lock(struct super_block *); - -extern int nilfs_construct_segment(struct super_block *); -extern int nilfs_construct_dsync_segment(struct super_block *, struct inode *, - loff_t, loff_t); -extern void nilfs_flush_segment(struct super_block *, ino_t); -extern int nilfs_clean_segments(struct super_block *, struct nilfs_argv *, - void **); - -int nilfs_attach_log_writer(struct super_block *sb, struct nilfs_root *root); -void nilfs_detach_log_writer(struct super_block *sb); - -/* recovery.c */ -extern int nilfs_read_super_root_block(struct the_nilfs *, sector_t, - struct buffer_head **, int); -extern int nilfs_search_super_root(struct the_nilfs *, - struct nilfs_recovery_info *); -int nilfs_salvage_orphan_logs(struct the_nilfs *nilfs, struct super_block *sb, - struct nilfs_recovery_info *ri); -extern void nilfs_dispose_segment_list(struct list_head *); - -#endif /* _NILFS_SEGMENT_H */ diff --git a/ANDROID_3.4.5/fs/nilfs2/sufile.c b/ANDROID_3.4.5/fs/nilfs2/sufile.c deleted file mode 100644 index c5b7653a..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/sufile.c +++ /dev/null @@ -1,921 +0,0 @@ -/* - * sufile.c - NILFS segment usage file. - * - * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Koji Sato <koji@osrg.net>. - * Revised by Ryusuke Konishi <ryusuke@osrg.net>. - */ - -#include <linux/kernel.h> -#include <linux/fs.h> -#include <linux/string.h> -#include <linux/buffer_head.h> -#include <linux/errno.h> -#include <linux/nilfs2_fs.h> -#include "mdt.h" -#include "sufile.h" - - -struct nilfs_sufile_info { - struct nilfs_mdt_info mi; - unsigned long ncleansegs;/* number of clean segments */ - __u64 allocmin; /* lower limit of allocatable segment range */ - __u64 allocmax; /* upper limit of allocatable segment range */ -}; - -static inline struct nilfs_sufile_info *NILFS_SUI(struct inode *sufile) -{ - return (struct nilfs_sufile_info *)NILFS_MDT(sufile); -} - -static inline unsigned long -nilfs_sufile_segment_usages_per_block(const struct inode *sufile) -{ - return NILFS_MDT(sufile)->mi_entries_per_block; -} - -static unsigned long -nilfs_sufile_get_blkoff(const struct inode *sufile, __u64 segnum) -{ - __u64 t = segnum + NILFS_MDT(sufile)->mi_first_entry_offset; - do_div(t, nilfs_sufile_segment_usages_per_block(sufile)); - return (unsigned long)t; -} - -static unsigned long -nilfs_sufile_get_offset(const struct inode *sufile, __u64 segnum) -{ - __u64 t = segnum + NILFS_MDT(sufile)->mi_first_entry_offset; - return do_div(t, nilfs_sufile_segment_usages_per_block(sufile)); -} - -static unsigned long -nilfs_sufile_segment_usages_in_block(const struct inode *sufile, __u64 curr, - __u64 max) -{ - return min_t(unsigned long, - nilfs_sufile_segment_usages_per_block(sufile) - - nilfs_sufile_get_offset(sufile, curr), - max - curr + 1); -} - -static struct nilfs_segment_usage * -nilfs_sufile_block_get_segment_usage(const struct inode *sufile, __u64 segnum, - struct buffer_head *bh, void *kaddr) -{ - return kaddr + bh_offset(bh) + - nilfs_sufile_get_offset(sufile, segnum) * - NILFS_MDT(sufile)->mi_entry_size; -} - -static inline int nilfs_sufile_get_header_block(struct inode *sufile, - struct buffer_head **bhp) -{ - return nilfs_mdt_get_block(sufile, 0, 0, NULL, bhp); -} - -static inline int -nilfs_sufile_get_segment_usage_block(struct inode *sufile, __u64 segnum, - int create, struct buffer_head **bhp) -{ - return nilfs_mdt_get_block(sufile, - nilfs_sufile_get_blkoff(sufile, segnum), - create, NULL, bhp); -} - -static int nilfs_sufile_delete_segment_usage_block(struct inode *sufile, - __u64 segnum) -{ - return nilfs_mdt_delete_block(sufile, - nilfs_sufile_get_blkoff(sufile, segnum)); -} - -static void nilfs_sufile_mod_counter(struct buffer_head *header_bh, - u64 ncleanadd, u64 ndirtyadd) -{ - struct nilfs_sufile_header *header; - void *kaddr; - - kaddr = kmap_atomic(header_bh->b_page); - header = kaddr + bh_offset(header_bh); - le64_add_cpu(&header->sh_ncleansegs, ncleanadd); - le64_add_cpu(&header->sh_ndirtysegs, ndirtyadd); - kunmap_atomic(kaddr); - - mark_buffer_dirty(header_bh); -} - -/** - * nilfs_sufile_get_ncleansegs - return the number of clean segments - * @sufile: inode of segment usage file - */ -unsigned long nilfs_sufile_get_ncleansegs(struct inode *sufile) -{ - return NILFS_SUI(sufile)->ncleansegs; -} - -/** - * nilfs_sufile_updatev - modify multiple segment usages at a time - * @sufile: inode of segment usage file - * @segnumv: array of segment numbers - * @nsegs: size of @segnumv array - * @create: creation flag - * @ndone: place to store number of modified segments on @segnumv - * @dofunc: primitive operation for the update - * - * Description: nilfs_sufile_updatev() repeatedly calls @dofunc - * against the given array of segments. The @dofunc is called with - * buffers of a header block and the sufile block in which the target - * segment usage entry is contained. If @ndone is given, the number - * of successfully modified segments from the head is stored in the - * place @ndone points to. - * - * Return Value: On success, zero is returned. On error, one of the - * following negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - * - * %-ENOENT - Given segment usage is in hole block (may be returned if - * @create is zero) - * - * %-EINVAL - Invalid segment usage number - */ -int nilfs_sufile_updatev(struct inode *sufile, __u64 *segnumv, size_t nsegs, - int create, size_t *ndone, - void (*dofunc)(struct inode *, __u64, - struct buffer_head *, - struct buffer_head *)) -{ - struct buffer_head *header_bh, *bh; - unsigned long blkoff, prev_blkoff; - __u64 *seg; - size_t nerr = 0, n = 0; - int ret = 0; - - if (unlikely(nsegs == 0)) - goto out; - - down_write(&NILFS_MDT(sufile)->mi_sem); - for (seg = segnumv; seg < segnumv + nsegs; seg++) { - if (unlikely(*seg >= nilfs_sufile_get_nsegments(sufile))) { - printk(KERN_WARNING - "%s: invalid segment number: %llu\n", __func__, - (unsigned long long)*seg); - nerr++; - } - } - if (nerr > 0) { - ret = -EINVAL; - goto out_sem; - } - - ret = nilfs_sufile_get_header_block(sufile, &header_bh); - if (ret < 0) - goto out_sem; - - seg = segnumv; - blkoff = nilfs_sufile_get_blkoff(sufile, *seg); - ret = nilfs_mdt_get_block(sufile, blkoff, create, NULL, &bh); - if (ret < 0) - goto out_header; - - for (;;) { - dofunc(sufile, *seg, header_bh, bh); - - if (++seg >= segnumv + nsegs) - break; - prev_blkoff = blkoff; - blkoff = nilfs_sufile_get_blkoff(sufile, *seg); - if (blkoff == prev_blkoff) - continue; - - /* get different block */ - brelse(bh); - ret = nilfs_mdt_get_block(sufile, blkoff, create, NULL, &bh); - if (unlikely(ret < 0)) - goto out_header; - } - brelse(bh); - - out_header: - n = seg - segnumv; - brelse(header_bh); - out_sem: - up_write(&NILFS_MDT(sufile)->mi_sem); - out: - if (ndone) - *ndone = n; - return ret; -} - -int nilfs_sufile_update(struct inode *sufile, __u64 segnum, int create, - void (*dofunc)(struct inode *, __u64, - struct buffer_head *, - struct buffer_head *)) -{ - struct buffer_head *header_bh, *bh; - int ret; - - if (unlikely(segnum >= nilfs_sufile_get_nsegments(sufile))) { - printk(KERN_WARNING "%s: invalid segment number: %llu\n", - __func__, (unsigned long long)segnum); - return -EINVAL; - } - down_write(&NILFS_MDT(sufile)->mi_sem); - - ret = nilfs_sufile_get_header_block(sufile, &header_bh); - if (ret < 0) - goto out_sem; - - ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, create, &bh); - if (!ret) { - dofunc(sufile, segnum, header_bh, bh); - brelse(bh); - } - brelse(header_bh); - - out_sem: - up_write(&NILFS_MDT(sufile)->mi_sem); - return ret; -} - -/** - * nilfs_sufile_set_alloc_range - limit range of segment to be allocated - * @sufile: inode of segment usage file - * @start: minimum segment number of allocatable region (inclusive) - * @end: maximum segment number of allocatable region (inclusive) - * - * Return Value: On success, 0 is returned. On error, one of the - * following negative error codes is returned. - * - * %-ERANGE - invalid segment region - */ -int nilfs_sufile_set_alloc_range(struct inode *sufile, __u64 start, __u64 end) -{ - struct nilfs_sufile_info *sui = NILFS_SUI(sufile); - __u64 nsegs; - int ret = -ERANGE; - - down_write(&NILFS_MDT(sufile)->mi_sem); - nsegs = nilfs_sufile_get_nsegments(sufile); - - if (start <= end && end < nsegs) { - sui->allocmin = start; - sui->allocmax = end; - ret = 0; - } - up_write(&NILFS_MDT(sufile)->mi_sem); - return ret; -} - -/** - * nilfs_sufile_alloc - allocate a segment - * @sufile: inode of segment usage file - * @segnump: pointer to segment number - * - * Description: nilfs_sufile_alloc() allocates a clean segment. - * - * Return Value: On success, 0 is returned and the segment number of the - * allocated segment is stored in the place pointed by @segnump. On error, one - * of the following negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - * - * %-ENOSPC - No clean segment left. - */ -int nilfs_sufile_alloc(struct inode *sufile, __u64 *segnump) -{ - struct buffer_head *header_bh, *su_bh; - struct nilfs_sufile_header *header; - struct nilfs_segment_usage *su; - struct nilfs_sufile_info *sui = NILFS_SUI(sufile); - size_t susz = NILFS_MDT(sufile)->mi_entry_size; - __u64 segnum, maxsegnum, last_alloc; - void *kaddr; - unsigned long nsegments, ncleansegs, nsus, cnt; - int ret, j; - - down_write(&NILFS_MDT(sufile)->mi_sem); - - ret = nilfs_sufile_get_header_block(sufile, &header_bh); - if (ret < 0) - goto out_sem; - kaddr = kmap_atomic(header_bh->b_page); - header = kaddr + bh_offset(header_bh); - ncleansegs = le64_to_cpu(header->sh_ncleansegs); - last_alloc = le64_to_cpu(header->sh_last_alloc); - kunmap_atomic(kaddr); - - nsegments = nilfs_sufile_get_nsegments(sufile); - maxsegnum = sui->allocmax; - segnum = last_alloc + 1; - if (segnum < sui->allocmin || segnum > sui->allocmax) - segnum = sui->allocmin; - - for (cnt = 0; cnt < nsegments; cnt += nsus) { - if (segnum > maxsegnum) { - if (cnt < sui->allocmax - sui->allocmin + 1) { - /* - * wrap around in the limited region. - * if allocation started from - * sui->allocmin, this never happens. - */ - segnum = sui->allocmin; - maxsegnum = last_alloc; - } else if (segnum > sui->allocmin && - sui->allocmax + 1 < nsegments) { - segnum = sui->allocmax + 1; - maxsegnum = nsegments - 1; - } else if (sui->allocmin > 0) { - segnum = 0; - maxsegnum = sui->allocmin - 1; - } else { - break; /* never happens */ - } - } - ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 1, - &su_bh); - if (ret < 0) - goto out_header; - kaddr = kmap_atomic(su_bh->b_page); - su = nilfs_sufile_block_get_segment_usage( - sufile, segnum, su_bh, kaddr); - - nsus = nilfs_sufile_segment_usages_in_block( - sufile, segnum, maxsegnum); - for (j = 0; j < nsus; j++, su = (void *)su + susz, segnum++) { - if (!nilfs_segment_usage_clean(su)) - continue; - /* found a clean segment */ - nilfs_segment_usage_set_dirty(su); - kunmap_atomic(kaddr); - - kaddr = kmap_atomic(header_bh->b_page); - header = kaddr + bh_offset(header_bh); - le64_add_cpu(&header->sh_ncleansegs, -1); - le64_add_cpu(&header->sh_ndirtysegs, 1); - header->sh_last_alloc = cpu_to_le64(segnum); - kunmap_atomic(kaddr); - - sui->ncleansegs--; - mark_buffer_dirty(header_bh); - mark_buffer_dirty(su_bh); - nilfs_mdt_mark_dirty(sufile); - brelse(su_bh); - *segnump = segnum; - goto out_header; - } - - kunmap_atomic(kaddr); - brelse(su_bh); - } - - /* no segments left */ - ret = -ENOSPC; - - out_header: - brelse(header_bh); - - out_sem: - up_write(&NILFS_MDT(sufile)->mi_sem); - return ret; -} - -void nilfs_sufile_do_cancel_free(struct inode *sufile, __u64 segnum, - struct buffer_head *header_bh, - struct buffer_head *su_bh) -{ - struct nilfs_segment_usage *su; - void *kaddr; - - kaddr = kmap_atomic(su_bh->b_page); - su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr); - if (unlikely(!nilfs_segment_usage_clean(su))) { - printk(KERN_WARNING "%s: segment %llu must be clean\n", - __func__, (unsigned long long)segnum); - kunmap_atomic(kaddr); - return; - } - nilfs_segment_usage_set_dirty(su); - kunmap_atomic(kaddr); - - nilfs_sufile_mod_counter(header_bh, -1, 1); - NILFS_SUI(sufile)->ncleansegs--; - - mark_buffer_dirty(su_bh); - nilfs_mdt_mark_dirty(sufile); -} - -void nilfs_sufile_do_scrap(struct inode *sufile, __u64 segnum, - struct buffer_head *header_bh, - struct buffer_head *su_bh) -{ - struct nilfs_segment_usage *su; - void *kaddr; - int clean, dirty; - - kaddr = kmap_atomic(su_bh->b_page); - su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr); - if (su->su_flags == cpu_to_le32(1UL << NILFS_SEGMENT_USAGE_DIRTY) && - su->su_nblocks == cpu_to_le32(0)) { - kunmap_atomic(kaddr); - return; - } - clean = nilfs_segment_usage_clean(su); - dirty = nilfs_segment_usage_dirty(su); - - /* make the segment garbage */ - su->su_lastmod = cpu_to_le64(0); - su->su_nblocks = cpu_to_le32(0); - su->su_flags = cpu_to_le32(1UL << NILFS_SEGMENT_USAGE_DIRTY); - kunmap_atomic(kaddr); - - nilfs_sufile_mod_counter(header_bh, clean ? (u64)-1 : 0, dirty ? 0 : 1); - NILFS_SUI(sufile)->ncleansegs -= clean; - - mark_buffer_dirty(su_bh); - nilfs_mdt_mark_dirty(sufile); -} - -void nilfs_sufile_do_free(struct inode *sufile, __u64 segnum, - struct buffer_head *header_bh, - struct buffer_head *su_bh) -{ - struct nilfs_segment_usage *su; - void *kaddr; - int sudirty; - - kaddr = kmap_atomic(su_bh->b_page); - su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr); - if (nilfs_segment_usage_clean(su)) { - printk(KERN_WARNING "%s: segment %llu is already clean\n", - __func__, (unsigned long long)segnum); - kunmap_atomic(kaddr); - return; - } - WARN_ON(nilfs_segment_usage_error(su)); - WARN_ON(!nilfs_segment_usage_dirty(su)); - - sudirty = nilfs_segment_usage_dirty(su); - nilfs_segment_usage_set_clean(su); - kunmap_atomic(kaddr); - mark_buffer_dirty(su_bh); - - nilfs_sufile_mod_counter(header_bh, 1, sudirty ? (u64)-1 : 0); - NILFS_SUI(sufile)->ncleansegs++; - - nilfs_mdt_mark_dirty(sufile); -} - -/** - * nilfs_sufile_mark_dirty - mark the buffer having a segment usage dirty - * @sufile: inode of segment usage file - * @segnum: segment number - */ -int nilfs_sufile_mark_dirty(struct inode *sufile, __u64 segnum) -{ - struct buffer_head *bh; - int ret; - - ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0, &bh); - if (!ret) { - mark_buffer_dirty(bh); - nilfs_mdt_mark_dirty(sufile); - brelse(bh); - } - return ret; -} - -/** - * nilfs_sufile_set_segment_usage - set usage of a segment - * @sufile: inode of segment usage file - * @segnum: segment number - * @nblocks: number of live blocks in the segment - * @modtime: modification time (option) - */ -int nilfs_sufile_set_segment_usage(struct inode *sufile, __u64 segnum, - unsigned long nblocks, time_t modtime) -{ - struct buffer_head *bh; - struct nilfs_segment_usage *su; - void *kaddr; - int ret; - - down_write(&NILFS_MDT(sufile)->mi_sem); - ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0, &bh); - if (ret < 0) - goto out_sem; - - kaddr = kmap_atomic(bh->b_page); - su = nilfs_sufile_block_get_segment_usage(sufile, segnum, bh, kaddr); - WARN_ON(nilfs_segment_usage_error(su)); - if (modtime) - su->su_lastmod = cpu_to_le64(modtime); - su->su_nblocks = cpu_to_le32(nblocks); - kunmap_atomic(kaddr); - - mark_buffer_dirty(bh); - nilfs_mdt_mark_dirty(sufile); - brelse(bh); - - out_sem: - up_write(&NILFS_MDT(sufile)->mi_sem); - return ret; -} - -/** - * nilfs_sufile_get_stat - get segment usage statistics - * @sufile: inode of segment usage file - * @stat: pointer to a structure of segment usage statistics - * - * Description: nilfs_sufile_get_stat() returns information about segment - * usage. - * - * Return Value: On success, 0 is returned, and segment usage information is - * stored in the place pointed by @stat. On error, one of the following - * negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - */ -int nilfs_sufile_get_stat(struct inode *sufile, struct nilfs_sustat *sustat) -{ - struct buffer_head *header_bh; - struct nilfs_sufile_header *header; - struct the_nilfs *nilfs = sufile->i_sb->s_fs_info; - void *kaddr; - int ret; - - down_read(&NILFS_MDT(sufile)->mi_sem); - - ret = nilfs_sufile_get_header_block(sufile, &header_bh); - if (ret < 0) - goto out_sem; - - kaddr = kmap_atomic(header_bh->b_page); - header = kaddr + bh_offset(header_bh); - sustat->ss_nsegs = nilfs_sufile_get_nsegments(sufile); - sustat->ss_ncleansegs = le64_to_cpu(header->sh_ncleansegs); - sustat->ss_ndirtysegs = le64_to_cpu(header->sh_ndirtysegs); - sustat->ss_ctime = nilfs->ns_ctime; - sustat->ss_nongc_ctime = nilfs->ns_nongc_ctime; - spin_lock(&nilfs->ns_last_segment_lock); - sustat->ss_prot_seq = nilfs->ns_prot_seq; - spin_unlock(&nilfs->ns_last_segment_lock); - kunmap_atomic(kaddr); - brelse(header_bh); - - out_sem: - up_read(&NILFS_MDT(sufile)->mi_sem); - return ret; -} - -void nilfs_sufile_do_set_error(struct inode *sufile, __u64 segnum, - struct buffer_head *header_bh, - struct buffer_head *su_bh) -{ - struct nilfs_segment_usage *su; - void *kaddr; - int suclean; - - kaddr = kmap_atomic(su_bh->b_page); - su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr); - if (nilfs_segment_usage_error(su)) { - kunmap_atomic(kaddr); - return; - } - suclean = nilfs_segment_usage_clean(su); - nilfs_segment_usage_set_error(su); - kunmap_atomic(kaddr); - - if (suclean) { - nilfs_sufile_mod_counter(header_bh, -1, 0); - NILFS_SUI(sufile)->ncleansegs--; - } - mark_buffer_dirty(su_bh); - nilfs_mdt_mark_dirty(sufile); -} - -/** - * nilfs_sufile_truncate_range - truncate range of segment array - * @sufile: inode of segment usage file - * @start: start segment number (inclusive) - * @end: end segment number (inclusive) - * - * Return Value: On success, 0 is returned. On error, one of the - * following negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - * - * %-EINVAL - Invalid number of segments specified - * - * %-EBUSY - Dirty or active segments are present in the range - */ -static int nilfs_sufile_truncate_range(struct inode *sufile, - __u64 start, __u64 end) -{ - struct the_nilfs *nilfs = sufile->i_sb->s_fs_info; - struct buffer_head *header_bh; - struct buffer_head *su_bh; - struct nilfs_segment_usage *su, *su2; - size_t susz = NILFS_MDT(sufile)->mi_entry_size; - unsigned long segusages_per_block; - unsigned long nsegs, ncleaned; - __u64 segnum; - void *kaddr; - ssize_t n, nc; - int ret; - int j; - - nsegs = nilfs_sufile_get_nsegments(sufile); - - ret = -EINVAL; - if (start > end || start >= nsegs) - goto out; - - ret = nilfs_sufile_get_header_block(sufile, &header_bh); - if (ret < 0) - goto out; - - segusages_per_block = nilfs_sufile_segment_usages_per_block(sufile); - ncleaned = 0; - - for (segnum = start; segnum <= end; segnum += n) { - n = min_t(unsigned long, - segusages_per_block - - nilfs_sufile_get_offset(sufile, segnum), - end - segnum + 1); - ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0, - &su_bh); - if (ret < 0) { - if (ret != -ENOENT) - goto out_header; - /* hole */ - continue; - } - kaddr = kmap_atomic(su_bh->b_page); - su = nilfs_sufile_block_get_segment_usage( - sufile, segnum, su_bh, kaddr); - su2 = su; - for (j = 0; j < n; j++, su = (void *)su + susz) { - if ((le32_to_cpu(su->su_flags) & - ~(1UL << NILFS_SEGMENT_USAGE_ERROR)) || - nilfs_segment_is_active(nilfs, segnum + j)) { - ret = -EBUSY; - kunmap_atomic(kaddr); - brelse(su_bh); - goto out_header; - } - } - nc = 0; - for (su = su2, j = 0; j < n; j++, su = (void *)su + susz) { - if (nilfs_segment_usage_error(su)) { - nilfs_segment_usage_set_clean(su); - nc++; - } - } - kunmap_atomic(kaddr); - if (nc > 0) { - mark_buffer_dirty(su_bh); - ncleaned += nc; - } - brelse(su_bh); - - if (n == segusages_per_block) { - /* make hole */ - nilfs_sufile_delete_segment_usage_block(sufile, segnum); - } - } - ret = 0; - -out_header: - if (ncleaned > 0) { - NILFS_SUI(sufile)->ncleansegs += ncleaned; - nilfs_sufile_mod_counter(header_bh, ncleaned, 0); - nilfs_mdt_mark_dirty(sufile); - } - brelse(header_bh); -out: - return ret; -} - -/** - * nilfs_sufile_resize - resize segment array - * @sufile: inode of segment usage file - * @newnsegs: new number of segments - * - * Return Value: On success, 0 is returned. On error, one of the - * following negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - * - * %-ENOSPC - Enough free space is not left for shrinking - * - * %-EBUSY - Dirty or active segments exist in the region to be truncated - */ -int nilfs_sufile_resize(struct inode *sufile, __u64 newnsegs) -{ - struct the_nilfs *nilfs = sufile->i_sb->s_fs_info; - struct buffer_head *header_bh; - struct nilfs_sufile_header *header; - struct nilfs_sufile_info *sui = NILFS_SUI(sufile); - void *kaddr; - unsigned long nsegs, nrsvsegs; - int ret = 0; - - down_write(&NILFS_MDT(sufile)->mi_sem); - - nsegs = nilfs_sufile_get_nsegments(sufile); - if (nsegs == newnsegs) - goto out; - - ret = -ENOSPC; - nrsvsegs = nilfs_nrsvsegs(nilfs, newnsegs); - if (newnsegs < nsegs && nsegs - newnsegs + nrsvsegs > sui->ncleansegs) - goto out; - - ret = nilfs_sufile_get_header_block(sufile, &header_bh); - if (ret < 0) - goto out; - - if (newnsegs > nsegs) { - sui->ncleansegs += newnsegs - nsegs; - } else /* newnsegs < nsegs */ { - ret = nilfs_sufile_truncate_range(sufile, newnsegs, nsegs - 1); - if (ret < 0) - goto out_header; - - sui->ncleansegs -= nsegs - newnsegs; - } - - kaddr = kmap_atomic(header_bh->b_page); - header = kaddr + bh_offset(header_bh); - header->sh_ncleansegs = cpu_to_le64(sui->ncleansegs); - kunmap_atomic(kaddr); - - mark_buffer_dirty(header_bh); - nilfs_mdt_mark_dirty(sufile); - nilfs_set_nsegments(nilfs, newnsegs); - -out_header: - brelse(header_bh); -out: - up_write(&NILFS_MDT(sufile)->mi_sem); - return ret; -} - -/** - * nilfs_sufile_get_suinfo - - * @sufile: inode of segment usage file - * @segnum: segment number to start looking - * @buf: array of suinfo - * @sisz: byte size of suinfo - * @nsi: size of suinfo array - * - * Description: - * - * Return Value: On success, 0 is returned and .... On error, one of the - * following negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - */ -ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum, void *buf, - unsigned sisz, size_t nsi) -{ - struct buffer_head *su_bh; - struct nilfs_segment_usage *su; - struct nilfs_suinfo *si = buf; - size_t susz = NILFS_MDT(sufile)->mi_entry_size; - struct the_nilfs *nilfs = sufile->i_sb->s_fs_info; - void *kaddr; - unsigned long nsegs, segusages_per_block; - ssize_t n; - int ret, i, j; - - down_read(&NILFS_MDT(sufile)->mi_sem); - - segusages_per_block = nilfs_sufile_segment_usages_per_block(sufile); - nsegs = min_t(unsigned long, - nilfs_sufile_get_nsegments(sufile) - segnum, - nsi); - for (i = 0; i < nsegs; i += n, segnum += n) { - n = min_t(unsigned long, - segusages_per_block - - nilfs_sufile_get_offset(sufile, segnum), - nsegs - i); - ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0, - &su_bh); - if (ret < 0) { - if (ret != -ENOENT) - goto out; - /* hole */ - memset(si, 0, sisz * n); - si = (void *)si + sisz * n; - continue; - } - - kaddr = kmap_atomic(su_bh->b_page); - su = nilfs_sufile_block_get_segment_usage( - sufile, segnum, su_bh, kaddr); - for (j = 0; j < n; - j++, su = (void *)su + susz, si = (void *)si + sisz) { - si->sui_lastmod = le64_to_cpu(su->su_lastmod); - si->sui_nblocks = le32_to_cpu(su->su_nblocks); - si->sui_flags = le32_to_cpu(su->su_flags) & - ~(1UL << NILFS_SEGMENT_USAGE_ACTIVE); - if (nilfs_segment_is_active(nilfs, segnum + j)) - si->sui_flags |= - (1UL << NILFS_SEGMENT_USAGE_ACTIVE); - } - kunmap_atomic(kaddr); - brelse(su_bh); - } - ret = nsegs; - - out: - up_read(&NILFS_MDT(sufile)->mi_sem); - return ret; -} - -/** - * nilfs_sufile_read - read or get sufile inode - * @sb: super block instance - * @susize: size of a segment usage entry - * @raw_inode: on-disk sufile inode - * @inodep: buffer to store the inode - */ -int nilfs_sufile_read(struct super_block *sb, size_t susize, - struct nilfs_inode *raw_inode, struct inode **inodep) -{ - struct inode *sufile; - struct nilfs_sufile_info *sui; - struct buffer_head *header_bh; - struct nilfs_sufile_header *header; - void *kaddr; - int err; - - sufile = nilfs_iget_locked(sb, NULL, NILFS_SUFILE_INO); - if (unlikely(!sufile)) - return -ENOMEM; - if (!(sufile->i_state & I_NEW)) - goto out; - - err = nilfs_mdt_init(sufile, NILFS_MDT_GFP, sizeof(*sui)); - if (err) - goto failed; - - nilfs_mdt_set_entry_size(sufile, susize, - sizeof(struct nilfs_sufile_header)); - - err = nilfs_read_inode_common(sufile, raw_inode); - if (err) - goto failed; - - err = nilfs_sufile_get_header_block(sufile, &header_bh); - if (err) - goto failed; - - sui = NILFS_SUI(sufile); - kaddr = kmap_atomic(header_bh->b_page); - header = kaddr + bh_offset(header_bh); - sui->ncleansegs = le64_to_cpu(header->sh_ncleansegs); - kunmap_atomic(kaddr); - brelse(header_bh); - - sui->allocmax = nilfs_sufile_get_nsegments(sufile) - 1; - sui->allocmin = 0; - - unlock_new_inode(sufile); - out: - *inodep = sufile; - return 0; - failed: - iget_failed(sufile); - return err; -} diff --git a/ANDROID_3.4.5/fs/nilfs2/sufile.h b/ANDROID_3.4.5/fs/nilfs2/sufile.h deleted file mode 100644 index e84bc5b5..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/sufile.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * sufile.h - NILFS segment usage file. - * - * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Koji Sato <koji@osrg.net>. - */ - -#ifndef _NILFS_SUFILE_H -#define _NILFS_SUFILE_H - -#include <linux/fs.h> -#include <linux/buffer_head.h> -#include <linux/nilfs2_fs.h> -#include "mdt.h" - - -static inline unsigned long nilfs_sufile_get_nsegments(struct inode *sufile) -{ - return ((struct the_nilfs *)sufile->i_sb->s_fs_info)->ns_nsegments; -} - -unsigned long nilfs_sufile_get_ncleansegs(struct inode *sufile); - -int nilfs_sufile_set_alloc_range(struct inode *sufile, __u64 start, __u64 end); -int nilfs_sufile_alloc(struct inode *, __u64 *); -int nilfs_sufile_mark_dirty(struct inode *sufile, __u64 segnum); -int nilfs_sufile_set_segment_usage(struct inode *sufile, __u64 segnum, - unsigned long nblocks, time_t modtime); -int nilfs_sufile_get_stat(struct inode *, struct nilfs_sustat *); -ssize_t nilfs_sufile_get_suinfo(struct inode *, __u64, void *, unsigned, - size_t); - -int nilfs_sufile_updatev(struct inode *, __u64 *, size_t, int, size_t *, - void (*dofunc)(struct inode *, __u64, - struct buffer_head *, - struct buffer_head *)); -int nilfs_sufile_update(struct inode *, __u64, int, - void (*dofunc)(struct inode *, __u64, - struct buffer_head *, - struct buffer_head *)); -void nilfs_sufile_do_scrap(struct inode *, __u64, struct buffer_head *, - struct buffer_head *); -void nilfs_sufile_do_free(struct inode *, __u64, struct buffer_head *, - struct buffer_head *); -void nilfs_sufile_do_cancel_free(struct inode *, __u64, struct buffer_head *, - struct buffer_head *); -void nilfs_sufile_do_set_error(struct inode *, __u64, struct buffer_head *, - struct buffer_head *); - -int nilfs_sufile_resize(struct inode *sufile, __u64 newnsegs); -int nilfs_sufile_read(struct super_block *sb, size_t susize, - struct nilfs_inode *raw_inode, struct inode **inodep); - -/** - * nilfs_sufile_scrap - make a segment garbage - * @sufile: inode of segment usage file - * @segnum: segment number to be freed - */ -static inline int nilfs_sufile_scrap(struct inode *sufile, __u64 segnum) -{ - return nilfs_sufile_update(sufile, segnum, 1, nilfs_sufile_do_scrap); -} - -/** - * nilfs_sufile_free - free segment - * @sufile: inode of segment usage file - * @segnum: segment number to be freed - */ -static inline int nilfs_sufile_free(struct inode *sufile, __u64 segnum) -{ - return nilfs_sufile_update(sufile, segnum, 0, nilfs_sufile_do_free); -} - -/** - * nilfs_sufile_freev - free segments - * @sufile: inode of segment usage file - * @segnumv: array of segment numbers - * @nsegs: size of @segnumv array - * @ndone: place to store the number of freed segments - */ -static inline int nilfs_sufile_freev(struct inode *sufile, __u64 *segnumv, - size_t nsegs, size_t *ndone) -{ - return nilfs_sufile_updatev(sufile, segnumv, nsegs, 0, ndone, - nilfs_sufile_do_free); -} - -/** - * nilfs_sufile_cancel_freev - reallocate freeing segments - * @sufile: inode of segment usage file - * @segnumv: array of segment numbers - * @nsegs: size of @segnumv array - * @ndone: place to store the number of cancelled segments - * - * Return Value: On success, 0 is returned. On error, a negative error codes - * is returned. - */ -static inline int nilfs_sufile_cancel_freev(struct inode *sufile, - __u64 *segnumv, size_t nsegs, - size_t *ndone) -{ - return nilfs_sufile_updatev(sufile, segnumv, nsegs, 0, ndone, - nilfs_sufile_do_cancel_free); -} - -/** - * nilfs_sufile_set_error - mark a segment as erroneous - * @sufile: inode of segment usage file - * @segnum: segment number - * - * Description: nilfs_sufile_set_error() marks the segment specified by - * @segnum as erroneous. The error segment will never be used again. - * - * Return Value: On success, 0 is returned. On error, one of the following - * negative error codes is returned. - * - * %-EIO - I/O error. - * - * %-ENOMEM - Insufficient amount of memory available. - * - * %-EINVAL - Invalid segment usage number. - */ -static inline int nilfs_sufile_set_error(struct inode *sufile, __u64 segnum) -{ - return nilfs_sufile_update(sufile, segnum, 0, - nilfs_sufile_do_set_error); -} - -#endif /* _NILFS_SUFILE_H */ diff --git a/ANDROID_3.4.5/fs/nilfs2/super.c b/ANDROID_3.4.5/fs/nilfs2/super.c deleted file mode 100644 index 1099a76c..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/super.c +++ /dev/null @@ -1,1460 +0,0 @@ -/* - * super.c - NILFS module and super block management. - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Ryusuke Konishi <ryusuke@osrg.net> - */ -/* - * linux/fs/ext2/super.c - * - * Copyright (C) 1992, 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) - * - * from - * - * linux/fs/minix/inode.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * - * Big-endian to little-endian byte-swapping/bitmaps by - * David S. Miller (davem@caip.rutgers.edu), 1995 - */ - -#include <linux/module.h> -#include <linux/string.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/blkdev.h> -#include <linux/parser.h> -#include <linux/crc32.h> -#include <linux/vfs.h> -#include <linux/writeback.h> -#include <linux/seq_file.h> -#include <linux/mount.h> -#include "nilfs.h" -#include "export.h" -#include "mdt.h" -#include "alloc.h" -#include "btree.h" -#include "btnode.h" -#include "page.h" -#include "cpfile.h" -#include "sufile.h" /* nilfs_sufile_resize(), nilfs_sufile_set_alloc_range() */ -#include "ifile.h" -#include "dat.h" -#include "segment.h" -#include "segbuf.h" - -MODULE_AUTHOR("NTT Corp."); -MODULE_DESCRIPTION("A New Implementation of the Log-structured Filesystem " - "(NILFS)"); -MODULE_LICENSE("GPL"); - -static struct kmem_cache *nilfs_inode_cachep; -struct kmem_cache *nilfs_transaction_cachep; -struct kmem_cache *nilfs_segbuf_cachep; -struct kmem_cache *nilfs_btree_path_cache; - -static int nilfs_setup_super(struct super_block *sb, int is_mount); -static int nilfs_remount(struct super_block *sb, int *flags, char *data); - -static void nilfs_set_error(struct super_block *sb) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct nilfs_super_block **sbp; - - down_write(&nilfs->ns_sem); - if (!(nilfs->ns_mount_state & NILFS_ERROR_FS)) { - nilfs->ns_mount_state |= NILFS_ERROR_FS; - sbp = nilfs_prepare_super(sb, 0); - if (likely(sbp)) { - sbp[0]->s_state |= cpu_to_le16(NILFS_ERROR_FS); - if (sbp[1]) - sbp[1]->s_state |= cpu_to_le16(NILFS_ERROR_FS); - nilfs_commit_super(sb, NILFS_SB_COMMIT_ALL); - } - } - up_write(&nilfs->ns_sem); -} - -/** - * nilfs_error() - report failure condition on a filesystem - * - * nilfs_error() sets an ERROR_FS flag on the superblock as well as - * reporting an error message. It should be called when NILFS detects - * incoherences or defects of meta data on disk. As for sustainable - * errors such as a single-shot I/O error, nilfs_warning() or the printk() - * function should be used instead. - * - * The segment constructor must not call this function because it can - * kill itself. - */ -void nilfs_error(struct super_block *sb, const char *function, - const char *fmt, ...) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct va_format vaf; - va_list args; - - va_start(args, fmt); - - vaf.fmt = fmt; - vaf.va = &args; - - printk(KERN_CRIT "NILFS error (device %s): %s: %pV\n", - sb->s_id, function, &vaf); - - va_end(args); - - if (!(sb->s_flags & MS_RDONLY)) { - nilfs_set_error(sb); - - if (nilfs_test_opt(nilfs, ERRORS_RO)) { - printk(KERN_CRIT "Remounting filesystem read-only\n"); - sb->s_flags |= MS_RDONLY; - } - } - - if (nilfs_test_opt(nilfs, ERRORS_PANIC)) - panic("NILFS (device %s): panic forced after error\n", - sb->s_id); -} - -void nilfs_warning(struct super_block *sb, const char *function, - const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - - va_start(args, fmt); - - vaf.fmt = fmt; - vaf.va = &args; - - printk(KERN_WARNING "NILFS warning (device %s): %s: %pV\n", - sb->s_id, function, &vaf); - - va_end(args); -} - - -struct inode *nilfs_alloc_inode(struct super_block *sb) -{ - struct nilfs_inode_info *ii; - - ii = kmem_cache_alloc(nilfs_inode_cachep, GFP_NOFS); - if (!ii) - return NULL; - ii->i_bh = NULL; - ii->i_state = 0; - ii->i_cno = 0; - ii->vfs_inode.i_version = 1; - nilfs_mapping_init(&ii->i_btnode_cache, &ii->vfs_inode, sb->s_bdi); - return &ii->vfs_inode; -} - -static void nilfs_i_callback(struct rcu_head *head) -{ - struct inode *inode = container_of(head, struct inode, i_rcu); - struct nilfs_mdt_info *mdi = NILFS_MDT(inode); - - if (mdi) { - kfree(mdi->mi_bgl); /* kfree(NULL) is safe */ - kfree(mdi); - } - kmem_cache_free(nilfs_inode_cachep, NILFS_I(inode)); -} - -void nilfs_destroy_inode(struct inode *inode) -{ - call_rcu(&inode->i_rcu, nilfs_i_callback); -} - -static int nilfs_sync_super(struct super_block *sb, int flag) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - int err; - - retry: - set_buffer_dirty(nilfs->ns_sbh[0]); - if (nilfs_test_opt(nilfs, BARRIER)) { - err = __sync_dirty_buffer(nilfs->ns_sbh[0], - WRITE_SYNC | WRITE_FLUSH_FUA); - } else { - err = sync_dirty_buffer(nilfs->ns_sbh[0]); - } - - if (unlikely(err)) { - printk(KERN_ERR - "NILFS: unable to write superblock (err=%d)\n", err); - if (err == -EIO && nilfs->ns_sbh[1]) { - /* - * sbp[0] points to newer log than sbp[1], - * so copy sbp[0] to sbp[1] to take over sbp[0]. - */ - memcpy(nilfs->ns_sbp[1], nilfs->ns_sbp[0], - nilfs->ns_sbsize); - nilfs_fall_back_super_block(nilfs); - goto retry; - } - } else { - struct nilfs_super_block *sbp = nilfs->ns_sbp[0]; - - nilfs->ns_sbwcount++; - - /* - * The latest segment becomes trailable from the position - * written in superblock. - */ - clear_nilfs_discontinued(nilfs); - - /* update GC protection for recent segments */ - if (nilfs->ns_sbh[1]) { - if (flag == NILFS_SB_COMMIT_ALL) { - set_buffer_dirty(nilfs->ns_sbh[1]); - if (sync_dirty_buffer(nilfs->ns_sbh[1]) < 0) - goto out; - } - if (le64_to_cpu(nilfs->ns_sbp[1]->s_last_cno) < - le64_to_cpu(nilfs->ns_sbp[0]->s_last_cno)) - sbp = nilfs->ns_sbp[1]; - } - - spin_lock(&nilfs->ns_last_segment_lock); - nilfs->ns_prot_seq = le64_to_cpu(sbp->s_last_seq); - spin_unlock(&nilfs->ns_last_segment_lock); - } - out: - return err; -} - -void nilfs_set_log_cursor(struct nilfs_super_block *sbp, - struct the_nilfs *nilfs) -{ - sector_t nfreeblocks; - - /* nilfs->ns_sem must be locked by the caller. */ - nilfs_count_free_blocks(nilfs, &nfreeblocks); - sbp->s_free_blocks_count = cpu_to_le64(nfreeblocks); - - spin_lock(&nilfs->ns_last_segment_lock); - sbp->s_last_seq = cpu_to_le64(nilfs->ns_last_seq); - sbp->s_last_pseg = cpu_to_le64(nilfs->ns_last_pseg); - sbp->s_last_cno = cpu_to_le64(nilfs->ns_last_cno); - spin_unlock(&nilfs->ns_last_segment_lock); -} - -struct nilfs_super_block **nilfs_prepare_super(struct super_block *sb, - int flip) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct nilfs_super_block **sbp = nilfs->ns_sbp; - - /* nilfs->ns_sem must be locked by the caller. */ - if (sbp[0]->s_magic != cpu_to_le16(NILFS_SUPER_MAGIC)) { - if (sbp[1] && - sbp[1]->s_magic == cpu_to_le16(NILFS_SUPER_MAGIC)) { - memcpy(sbp[0], sbp[1], nilfs->ns_sbsize); - } else { - printk(KERN_CRIT "NILFS: superblock broke on dev %s\n", - sb->s_id); - return NULL; - } - } else if (sbp[1] && - sbp[1]->s_magic != cpu_to_le16(NILFS_SUPER_MAGIC)) { - memcpy(sbp[1], sbp[0], nilfs->ns_sbsize); - } - - if (flip && sbp[1]) - nilfs_swap_super_block(nilfs); - - return sbp; -} - -int nilfs_commit_super(struct super_block *sb, int flag) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct nilfs_super_block **sbp = nilfs->ns_sbp; - time_t t; - - /* nilfs->ns_sem must be locked by the caller. */ - t = get_seconds(); - nilfs->ns_sbwtime = t; - sbp[0]->s_wtime = cpu_to_le64(t); - sbp[0]->s_sum = 0; - sbp[0]->s_sum = cpu_to_le32(crc32_le(nilfs->ns_crc_seed, - (unsigned char *)sbp[0], - nilfs->ns_sbsize)); - if (flag == NILFS_SB_COMMIT_ALL && sbp[1]) { - sbp[1]->s_wtime = sbp[0]->s_wtime; - sbp[1]->s_sum = 0; - sbp[1]->s_sum = cpu_to_le32(crc32_le(nilfs->ns_crc_seed, - (unsigned char *)sbp[1], - nilfs->ns_sbsize)); - } - clear_nilfs_sb_dirty(nilfs); - return nilfs_sync_super(sb, flag); -} - -/** - * nilfs_cleanup_super() - write filesystem state for cleanup - * @sb: super block instance to be unmounted or degraded to read-only - * - * This function restores state flags in the on-disk super block. - * This will set "clean" flag (i.e. NILFS_VALID_FS) unless the - * filesystem was not clean previously. - */ -int nilfs_cleanup_super(struct super_block *sb) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct nilfs_super_block **sbp; - int flag = NILFS_SB_COMMIT; - int ret = -EIO; - - sbp = nilfs_prepare_super(sb, 0); - if (sbp) { - sbp[0]->s_state = cpu_to_le16(nilfs->ns_mount_state); - nilfs_set_log_cursor(sbp[0], nilfs); - if (sbp[1] && sbp[0]->s_last_cno == sbp[1]->s_last_cno) { - /* - * make the "clean" flag also to the opposite - * super block if both super blocks point to - * the same checkpoint. - */ - sbp[1]->s_state = sbp[0]->s_state; - flag = NILFS_SB_COMMIT_ALL; - } - ret = nilfs_commit_super(sb, flag); - } - return ret; -} - -/** - * nilfs_move_2nd_super - relocate secondary super block - * @sb: super block instance - * @sb2off: new offset of the secondary super block (in bytes) - */ -static int nilfs_move_2nd_super(struct super_block *sb, loff_t sb2off) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct buffer_head *nsbh; - struct nilfs_super_block *nsbp; - sector_t blocknr, newblocknr; - unsigned long offset; - int sb2i = -1; /* array index of the secondary superblock */ - int ret = 0; - - /* nilfs->ns_sem must be locked by the caller. */ - if (nilfs->ns_sbh[1] && - nilfs->ns_sbh[1]->b_blocknr > nilfs->ns_first_data_block) { - sb2i = 1; - blocknr = nilfs->ns_sbh[1]->b_blocknr; - } else if (nilfs->ns_sbh[0]->b_blocknr > nilfs->ns_first_data_block) { - sb2i = 0; - blocknr = nilfs->ns_sbh[0]->b_blocknr; - } - if (sb2i >= 0 && (u64)blocknr << nilfs->ns_blocksize_bits == sb2off) - goto out; /* super block location is unchanged */ - - /* Get new super block buffer */ - newblocknr = sb2off >> nilfs->ns_blocksize_bits; - offset = sb2off & (nilfs->ns_blocksize - 1); - nsbh = sb_getblk(sb, newblocknr); - if (!nsbh) { - printk(KERN_WARNING - "NILFS warning: unable to move secondary superblock " - "to block %llu\n", (unsigned long long)newblocknr); - ret = -EIO; - goto out; - } - nsbp = (void *)nsbh->b_data + offset; - memset(nsbp, 0, nilfs->ns_blocksize); - - if (sb2i >= 0) { - memcpy(nsbp, nilfs->ns_sbp[sb2i], nilfs->ns_sbsize); - brelse(nilfs->ns_sbh[sb2i]); - nilfs->ns_sbh[sb2i] = nsbh; - nilfs->ns_sbp[sb2i] = nsbp; - } else if (nilfs->ns_sbh[0]->b_blocknr < nilfs->ns_first_data_block) { - /* secondary super block will be restored to index 1 */ - nilfs->ns_sbh[1] = nsbh; - nilfs->ns_sbp[1] = nsbp; - } else { - brelse(nsbh); - } -out: - return ret; -} - -/** - * nilfs_resize_fs - resize the filesystem - * @sb: super block instance - * @newsize: new size of the filesystem (in bytes) - */ -int nilfs_resize_fs(struct super_block *sb, __u64 newsize) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct nilfs_super_block **sbp; - __u64 devsize, newnsegs; - loff_t sb2off; - int ret; - - ret = -ERANGE; - devsize = i_size_read(sb->s_bdev->bd_inode); - if (newsize > devsize) - goto out; - - /* - * Write lock is required to protect some functions depending - * on the number of segments, the number of reserved segments, - * and so forth. - */ - down_write(&nilfs->ns_segctor_sem); - - sb2off = NILFS_SB2_OFFSET_BYTES(newsize); - newnsegs = sb2off >> nilfs->ns_blocksize_bits; - do_div(newnsegs, nilfs->ns_blocks_per_segment); - - ret = nilfs_sufile_resize(nilfs->ns_sufile, newnsegs); - up_write(&nilfs->ns_segctor_sem); - if (ret < 0) - goto out; - - ret = nilfs_construct_segment(sb); - if (ret < 0) - goto out; - - down_write(&nilfs->ns_sem); - nilfs_move_2nd_super(sb, sb2off); - ret = -EIO; - sbp = nilfs_prepare_super(sb, 0); - if (likely(sbp)) { - nilfs_set_log_cursor(sbp[0], nilfs); - /* - * Drop NILFS_RESIZE_FS flag for compatibility with - * mount-time resize which may be implemented in a - * future release. - */ - sbp[0]->s_state = cpu_to_le16(le16_to_cpu(sbp[0]->s_state) & - ~NILFS_RESIZE_FS); - sbp[0]->s_dev_size = cpu_to_le64(newsize); - sbp[0]->s_nsegments = cpu_to_le64(nilfs->ns_nsegments); - if (sbp[1]) - memcpy(sbp[1], sbp[0], nilfs->ns_sbsize); - ret = nilfs_commit_super(sb, NILFS_SB_COMMIT_ALL); - } - up_write(&nilfs->ns_sem); - - /* - * Reset the range of allocatable segments last. This order - * is important in the case of expansion because the secondary - * superblock must be protected from log write until migration - * completes. - */ - if (!ret) - nilfs_sufile_set_alloc_range(nilfs->ns_sufile, 0, newnsegs - 1); -out: - return ret; -} - -static void nilfs_put_super(struct super_block *sb) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - - nilfs_detach_log_writer(sb); - - if (!(sb->s_flags & MS_RDONLY)) { - down_write(&nilfs->ns_sem); - nilfs_cleanup_super(sb); - up_write(&nilfs->ns_sem); - } - - iput(nilfs->ns_sufile); - iput(nilfs->ns_cpfile); - iput(nilfs->ns_dat); - - destroy_nilfs(nilfs); - sb->s_fs_info = NULL; -} - -static int nilfs_sync_fs(struct super_block *sb, int wait) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct nilfs_super_block **sbp; - int err = 0; - - /* This function is called when super block should be written back */ - if (wait) - err = nilfs_construct_segment(sb); - - down_write(&nilfs->ns_sem); - if (nilfs_sb_dirty(nilfs)) { - sbp = nilfs_prepare_super(sb, nilfs_sb_will_flip(nilfs)); - if (likely(sbp)) { - nilfs_set_log_cursor(sbp[0], nilfs); - nilfs_commit_super(sb, NILFS_SB_COMMIT); - } - } - up_write(&nilfs->ns_sem); - - return err; -} - -int nilfs_attach_checkpoint(struct super_block *sb, __u64 cno, int curr_mnt, - struct nilfs_root **rootp) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct nilfs_root *root; - struct nilfs_checkpoint *raw_cp; - struct buffer_head *bh_cp; - int err = -ENOMEM; - - root = nilfs_find_or_create_root( - nilfs, curr_mnt ? NILFS_CPTREE_CURRENT_CNO : cno); - if (!root) - return err; - - if (root->ifile) - goto reuse; /* already attached checkpoint */ - - down_read(&nilfs->ns_segctor_sem); - err = nilfs_cpfile_get_checkpoint(nilfs->ns_cpfile, cno, 0, &raw_cp, - &bh_cp); - up_read(&nilfs->ns_segctor_sem); - if (unlikely(err)) { - if (err == -ENOENT || err == -EINVAL) { - printk(KERN_ERR - "NILFS: Invalid checkpoint " - "(checkpoint number=%llu)\n", - (unsigned long long)cno); - err = -EINVAL; - } - goto failed; - } - - err = nilfs_ifile_read(sb, root, nilfs->ns_inode_size, - &raw_cp->cp_ifile_inode, &root->ifile); - if (err) - goto failed_bh; - - atomic_set(&root->inodes_count, le64_to_cpu(raw_cp->cp_inodes_count)); - atomic_set(&root->blocks_count, le64_to_cpu(raw_cp->cp_blocks_count)); - - nilfs_cpfile_put_checkpoint(nilfs->ns_cpfile, cno, bh_cp); - - reuse: - *rootp = root; - return 0; - - failed_bh: - nilfs_cpfile_put_checkpoint(nilfs->ns_cpfile, cno, bh_cp); - failed: - nilfs_put_root(root); - - return err; -} - -static int nilfs_freeze(struct super_block *sb) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - int err; - - if (sb->s_flags & MS_RDONLY) - return 0; - - /* Mark super block clean */ - down_write(&nilfs->ns_sem); - err = nilfs_cleanup_super(sb); - up_write(&nilfs->ns_sem); - return err; -} - -static int nilfs_unfreeze(struct super_block *sb) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - - if (sb->s_flags & MS_RDONLY) - return 0; - - down_write(&nilfs->ns_sem); - nilfs_setup_super(sb, false); - up_write(&nilfs->ns_sem); - return 0; -} - -static int nilfs_statfs(struct dentry *dentry, struct kstatfs *buf) -{ - struct super_block *sb = dentry->d_sb; - struct nilfs_root *root = NILFS_I(dentry->d_inode)->i_root; - struct the_nilfs *nilfs = root->nilfs; - u64 id = huge_encode_dev(sb->s_bdev->bd_dev); - unsigned long long blocks; - unsigned long overhead; - unsigned long nrsvblocks; - sector_t nfreeblocks; - int err; - - /* - * Compute all of the segment blocks - * - * The blocks before first segment and after last segment - * are excluded. - */ - blocks = nilfs->ns_blocks_per_segment * nilfs->ns_nsegments - - nilfs->ns_first_data_block; - nrsvblocks = nilfs->ns_nrsvsegs * nilfs->ns_blocks_per_segment; - - /* - * Compute the overhead - * - * When distributing meta data blocks outside segment structure, - * We must count them as the overhead. - */ - overhead = 0; - - err = nilfs_count_free_blocks(nilfs, &nfreeblocks); - if (unlikely(err)) - return err; - - buf->f_type = NILFS_SUPER_MAGIC; - buf->f_bsize = sb->s_blocksize; - buf->f_blocks = blocks - overhead; - buf->f_bfree = nfreeblocks; - buf->f_bavail = (buf->f_bfree >= nrsvblocks) ? - (buf->f_bfree - nrsvblocks) : 0; - buf->f_files = atomic_read(&root->inodes_count); - buf->f_ffree = 0; /* nilfs_count_free_inodes(sb); */ - buf->f_namelen = NILFS_NAME_LEN; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); - - return 0; -} - -static int nilfs_show_options(struct seq_file *seq, struct dentry *dentry) -{ - struct super_block *sb = dentry->d_sb; - struct the_nilfs *nilfs = sb->s_fs_info; - struct nilfs_root *root = NILFS_I(dentry->d_inode)->i_root; - - if (!nilfs_test_opt(nilfs, BARRIER)) - seq_puts(seq, ",nobarrier"); - if (root->cno != NILFS_CPTREE_CURRENT_CNO) - seq_printf(seq, ",cp=%llu", (unsigned long long)root->cno); - if (nilfs_test_opt(nilfs, ERRORS_PANIC)) - seq_puts(seq, ",errors=panic"); - if (nilfs_test_opt(nilfs, ERRORS_CONT)) - seq_puts(seq, ",errors=continue"); - if (nilfs_test_opt(nilfs, STRICT_ORDER)) - seq_puts(seq, ",order=strict"); - if (nilfs_test_opt(nilfs, NORECOVERY)) - seq_puts(seq, ",norecovery"); - if (nilfs_test_opt(nilfs, DISCARD)) - seq_puts(seq, ",discard"); - - return 0; -} - -static const struct super_operations nilfs_sops = { - .alloc_inode = nilfs_alloc_inode, - .destroy_inode = nilfs_destroy_inode, - .dirty_inode = nilfs_dirty_inode, - /* .write_inode = nilfs_write_inode, */ - /* .put_inode = nilfs_put_inode, */ - /* .drop_inode = nilfs_drop_inode, */ - .evict_inode = nilfs_evict_inode, - .put_super = nilfs_put_super, - /* .write_super = nilfs_write_super, */ - .sync_fs = nilfs_sync_fs, - .freeze_fs = nilfs_freeze, - .unfreeze_fs = nilfs_unfreeze, - /* .write_super_lockfs */ - /* .unlockfs */ - .statfs = nilfs_statfs, - .remount_fs = nilfs_remount, - /* .umount_begin */ - .show_options = nilfs_show_options -}; - -enum { - Opt_err_cont, Opt_err_panic, Opt_err_ro, - Opt_barrier, Opt_nobarrier, Opt_snapshot, Opt_order, Opt_norecovery, - Opt_discard, Opt_nodiscard, Opt_err, -}; - -static match_table_t tokens = { - {Opt_err_cont, "errors=continue"}, - {Opt_err_panic, "errors=panic"}, - {Opt_err_ro, "errors=remount-ro"}, - {Opt_barrier, "barrier"}, - {Opt_nobarrier, "nobarrier"}, - {Opt_snapshot, "cp=%u"}, - {Opt_order, "order=%s"}, - {Opt_norecovery, "norecovery"}, - {Opt_discard, "discard"}, - {Opt_nodiscard, "nodiscard"}, - {Opt_err, NULL} -}; - -static int parse_options(char *options, struct super_block *sb, int is_remount) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - char *p; - substring_t args[MAX_OPT_ARGS]; - - if (!options) - return 1; - - while ((p = strsep(&options, ",")) != NULL) { - int token; - if (!*p) - continue; - - token = match_token(p, tokens, args); - switch (token) { - case Opt_barrier: - nilfs_set_opt(nilfs, BARRIER); - break; - case Opt_nobarrier: - nilfs_clear_opt(nilfs, BARRIER); - break; - case Opt_order: - if (strcmp(args[0].from, "relaxed") == 0) - /* Ordered data semantics */ - nilfs_clear_opt(nilfs, STRICT_ORDER); - else if (strcmp(args[0].from, "strict") == 0) - /* Strict in-order semantics */ - nilfs_set_opt(nilfs, STRICT_ORDER); - else - return 0; - break; - case Opt_err_panic: - nilfs_write_opt(nilfs, ERROR_MODE, ERRORS_PANIC); - break; - case Opt_err_ro: - nilfs_write_opt(nilfs, ERROR_MODE, ERRORS_RO); - break; - case Opt_err_cont: - nilfs_write_opt(nilfs, ERROR_MODE, ERRORS_CONT); - break; - case Opt_snapshot: - if (is_remount) { - printk(KERN_ERR - "NILFS: \"%s\" option is invalid " - "for remount.\n", p); - return 0; - } - break; - case Opt_norecovery: - nilfs_set_opt(nilfs, NORECOVERY); - break; - case Opt_discard: - nilfs_set_opt(nilfs, DISCARD); - break; - case Opt_nodiscard: - nilfs_clear_opt(nilfs, DISCARD); - break; - default: - printk(KERN_ERR - "NILFS: Unrecognized mount option \"%s\"\n", p); - return 0; - } - } - return 1; -} - -static inline void -nilfs_set_default_options(struct super_block *sb, - struct nilfs_super_block *sbp) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - - nilfs->ns_mount_opt = - NILFS_MOUNT_ERRORS_RO | NILFS_MOUNT_BARRIER; -} - -static int nilfs_setup_super(struct super_block *sb, int is_mount) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct nilfs_super_block **sbp; - int max_mnt_count; - int mnt_count; - - /* nilfs->ns_sem must be locked by the caller. */ - sbp = nilfs_prepare_super(sb, 0); - if (!sbp) - return -EIO; - - if (!is_mount) - goto skip_mount_setup; - - max_mnt_count = le16_to_cpu(sbp[0]->s_max_mnt_count); - mnt_count = le16_to_cpu(sbp[0]->s_mnt_count); - - if (nilfs->ns_mount_state & NILFS_ERROR_FS) { - printk(KERN_WARNING - "NILFS warning: mounting fs with errors\n"); -#if 0 - } else if (max_mnt_count >= 0 && mnt_count >= max_mnt_count) { - printk(KERN_WARNING - "NILFS warning: maximal mount count reached\n"); -#endif - } - if (!max_mnt_count) - sbp[0]->s_max_mnt_count = cpu_to_le16(NILFS_DFL_MAX_MNT_COUNT); - - sbp[0]->s_mnt_count = cpu_to_le16(mnt_count + 1); - sbp[0]->s_mtime = cpu_to_le64(get_seconds()); - -skip_mount_setup: - sbp[0]->s_state = - cpu_to_le16(le16_to_cpu(sbp[0]->s_state) & ~NILFS_VALID_FS); - /* synchronize sbp[1] with sbp[0] */ - if (sbp[1]) - memcpy(sbp[1], sbp[0], nilfs->ns_sbsize); - return nilfs_commit_super(sb, NILFS_SB_COMMIT_ALL); -} - -struct nilfs_super_block *nilfs_read_super_block(struct super_block *sb, - u64 pos, int blocksize, - struct buffer_head **pbh) -{ - unsigned long long sb_index = pos; - unsigned long offset; - - offset = do_div(sb_index, blocksize); - *pbh = sb_bread(sb, sb_index); - if (!*pbh) - return NULL; - return (struct nilfs_super_block *)((char *)(*pbh)->b_data + offset); -} - -int nilfs_store_magic_and_option(struct super_block *sb, - struct nilfs_super_block *sbp, - char *data) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - - sb->s_magic = le16_to_cpu(sbp->s_magic); - - /* FS independent flags */ -#ifdef NILFS_ATIME_DISABLE - sb->s_flags |= MS_NOATIME; -#endif - - nilfs_set_default_options(sb, sbp); - - nilfs->ns_resuid = le16_to_cpu(sbp->s_def_resuid); - nilfs->ns_resgid = le16_to_cpu(sbp->s_def_resgid); - nilfs->ns_interval = le32_to_cpu(sbp->s_c_interval); - nilfs->ns_watermark = le32_to_cpu(sbp->s_c_block_max); - - return !parse_options(data, sb, 0) ? -EINVAL : 0 ; -} - -int nilfs_check_feature_compatibility(struct super_block *sb, - struct nilfs_super_block *sbp) -{ - __u64 features; - - features = le64_to_cpu(sbp->s_feature_incompat) & - ~NILFS_FEATURE_INCOMPAT_SUPP; - if (features) { - printk(KERN_ERR "NILFS: couldn't mount because of unsupported " - "optional features (%llx)\n", - (unsigned long long)features); - return -EINVAL; - } - features = le64_to_cpu(sbp->s_feature_compat_ro) & - ~NILFS_FEATURE_COMPAT_RO_SUPP; - if (!(sb->s_flags & MS_RDONLY) && features) { - printk(KERN_ERR "NILFS: couldn't mount RDWR because of " - "unsupported optional features (%llx)\n", - (unsigned long long)features); - return -EINVAL; - } - return 0; -} - -static int nilfs_get_root_dentry(struct super_block *sb, - struct nilfs_root *root, - struct dentry **root_dentry) -{ - struct inode *inode; - struct dentry *dentry; - int ret = 0; - - inode = nilfs_iget(sb, root, NILFS_ROOT_INO); - if (IS_ERR(inode)) { - printk(KERN_ERR "NILFS: get root inode failed\n"); - ret = PTR_ERR(inode); - goto out; - } - if (!S_ISDIR(inode->i_mode) || !inode->i_blocks || !inode->i_size) { - iput(inode); - printk(KERN_ERR "NILFS: corrupt root inode.\n"); - ret = -EINVAL; - goto out; - } - - if (root->cno == NILFS_CPTREE_CURRENT_CNO) { - dentry = d_find_alias(inode); - if (!dentry) { - dentry = d_make_root(inode); - if (!dentry) { - ret = -ENOMEM; - goto failed_dentry; - } - } else { - iput(inode); - } - } else { - dentry = d_obtain_alias(inode); - if (IS_ERR(dentry)) { - ret = PTR_ERR(dentry); - goto failed_dentry; - } - } - *root_dentry = dentry; - out: - return ret; - - failed_dentry: - printk(KERN_ERR "NILFS: get root dentry failed\n"); - goto out; -} - -static int nilfs_attach_snapshot(struct super_block *s, __u64 cno, - struct dentry **root_dentry) -{ - struct the_nilfs *nilfs = s->s_fs_info; - struct nilfs_root *root; - int ret; - - down_read(&nilfs->ns_segctor_sem); - ret = nilfs_cpfile_is_snapshot(nilfs->ns_cpfile, cno); - up_read(&nilfs->ns_segctor_sem); - if (ret < 0) { - ret = (ret == -ENOENT) ? -EINVAL : ret; - goto out; - } else if (!ret) { - printk(KERN_ERR "NILFS: The specified checkpoint is " - "not a snapshot (checkpoint number=%llu).\n", - (unsigned long long)cno); - ret = -EINVAL; - goto out; - } - - ret = nilfs_attach_checkpoint(s, cno, false, &root); - if (ret) { - printk(KERN_ERR "NILFS: error loading snapshot " - "(checkpoint number=%llu).\n", - (unsigned long long)cno); - goto out; - } - ret = nilfs_get_root_dentry(s, root, root_dentry); - nilfs_put_root(root); - out: - return ret; -} - -static int nilfs_tree_was_touched(struct dentry *root_dentry) -{ - return root_dentry->d_count > 1; -} - -/** - * nilfs_try_to_shrink_tree() - try to shrink dentries of a checkpoint - * @root_dentry: root dentry of the tree to be shrunk - * - * This function returns true if the tree was in-use. - */ -static int nilfs_try_to_shrink_tree(struct dentry *root_dentry) -{ - if (have_submounts(root_dentry)) - return true; - shrink_dcache_parent(root_dentry); - return nilfs_tree_was_touched(root_dentry); -} - -int nilfs_checkpoint_is_mounted(struct super_block *sb, __u64 cno) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - struct nilfs_root *root; - struct inode *inode; - struct dentry *dentry; - int ret; - - if (cno < 0 || cno > nilfs->ns_cno) - return false; - - if (cno >= nilfs_last_cno(nilfs)) - return true; /* protect recent checkpoints */ - - ret = false; - root = nilfs_lookup_root(nilfs, cno); - if (root) { - inode = nilfs_ilookup(sb, root, NILFS_ROOT_INO); - if (inode) { - dentry = d_find_alias(inode); - if (dentry) { - if (nilfs_tree_was_touched(dentry)) - ret = nilfs_try_to_shrink_tree(dentry); - dput(dentry); - } - iput(inode); - } - nilfs_put_root(root); - } - return ret; -} - -/** - * nilfs_fill_super() - initialize a super block instance - * @sb: super_block - * @data: mount options - * @silent: silent mode flag - * - * This function is called exclusively by nilfs->ns_mount_mutex. - * So, the recovery process is protected from other simultaneous mounts. - */ -static int -nilfs_fill_super(struct super_block *sb, void *data, int silent) -{ - struct the_nilfs *nilfs; - struct nilfs_root *fsroot; - struct backing_dev_info *bdi; - __u64 cno; - int err; - - nilfs = alloc_nilfs(sb->s_bdev); - if (!nilfs) - return -ENOMEM; - - sb->s_fs_info = nilfs; - - err = init_nilfs(nilfs, sb, (char *)data); - if (err) - goto failed_nilfs; - - sb->s_op = &nilfs_sops; - sb->s_export_op = &nilfs_export_ops; - sb->s_root = NULL; - sb->s_time_gran = 1; - sb->s_max_links = NILFS_LINK_MAX; - - bdi = sb->s_bdev->bd_inode->i_mapping->backing_dev_info; - sb->s_bdi = bdi ? : &default_backing_dev_info; - - err = load_nilfs(nilfs, sb); - if (err) - goto failed_nilfs; - - cno = nilfs_last_cno(nilfs); - err = nilfs_attach_checkpoint(sb, cno, true, &fsroot); - if (err) { - printk(KERN_ERR "NILFS: error loading last checkpoint " - "(checkpoint number=%llu).\n", (unsigned long long)cno); - goto failed_unload; - } - - if (!(sb->s_flags & MS_RDONLY)) { - err = nilfs_attach_log_writer(sb, fsroot); - if (err) - goto failed_checkpoint; - } - - err = nilfs_get_root_dentry(sb, fsroot, &sb->s_root); - if (err) - goto failed_segctor; - - nilfs_put_root(fsroot); - - if (!(sb->s_flags & MS_RDONLY)) { - down_write(&nilfs->ns_sem); - nilfs_setup_super(sb, true); - up_write(&nilfs->ns_sem); - } - - return 0; - - failed_segctor: - nilfs_detach_log_writer(sb); - - failed_checkpoint: - nilfs_put_root(fsroot); - - failed_unload: - iput(nilfs->ns_sufile); - iput(nilfs->ns_cpfile); - iput(nilfs->ns_dat); - - failed_nilfs: - destroy_nilfs(nilfs); - return err; -} - -static int nilfs_remount(struct super_block *sb, int *flags, char *data) -{ - struct the_nilfs *nilfs = sb->s_fs_info; - unsigned long old_sb_flags; - unsigned long old_mount_opt; - int err; - - old_sb_flags = sb->s_flags; - old_mount_opt = nilfs->ns_mount_opt; - - if (!parse_options(data, sb, 1)) { - err = -EINVAL; - goto restore_opts; - } - sb->s_flags = (sb->s_flags & ~MS_POSIXACL); - - err = -EINVAL; - - if (!nilfs_valid_fs(nilfs)) { - printk(KERN_WARNING "NILFS (device %s): couldn't " - "remount because the filesystem is in an " - "incomplete recovery state.\n", sb->s_id); - goto restore_opts; - } - - if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) - goto out; - if (*flags & MS_RDONLY) { - /* Shutting down log writer */ - nilfs_detach_log_writer(sb); - sb->s_flags |= MS_RDONLY; - - /* - * Remounting a valid RW partition RDONLY, so set - * the RDONLY flag and then mark the partition as valid again. - */ - down_write(&nilfs->ns_sem); - nilfs_cleanup_super(sb); - up_write(&nilfs->ns_sem); - } else { - __u64 features; - struct nilfs_root *root; - - /* - * Mounting a RDONLY partition read-write, so reread and - * store the current valid flag. (It may have been changed - * by fsck since we originally mounted the partition.) - */ - down_read(&nilfs->ns_sem); - features = le64_to_cpu(nilfs->ns_sbp[0]->s_feature_compat_ro) & - ~NILFS_FEATURE_COMPAT_RO_SUPP; - up_read(&nilfs->ns_sem); - if (features) { - printk(KERN_WARNING "NILFS (device %s): couldn't " - "remount RDWR because of unsupported optional " - "features (%llx)\n", - sb->s_id, (unsigned long long)features); - err = -EROFS; - goto restore_opts; - } - - sb->s_flags &= ~MS_RDONLY; - - root = NILFS_I(sb->s_root->d_inode)->i_root; - err = nilfs_attach_log_writer(sb, root); - if (err) - goto restore_opts; - - down_write(&nilfs->ns_sem); - nilfs_setup_super(sb, true); - up_write(&nilfs->ns_sem); - } - out: - return 0; - - restore_opts: - sb->s_flags = old_sb_flags; - nilfs->ns_mount_opt = old_mount_opt; - return err; -} - -struct nilfs_super_data { - struct block_device *bdev; - __u64 cno; - int flags; -}; - -/** - * nilfs_identify - pre-read mount options needed to identify mount instance - * @data: mount options - * @sd: nilfs_super_data - */ -static int nilfs_identify(char *data, struct nilfs_super_data *sd) -{ - char *p, *options = data; - substring_t args[MAX_OPT_ARGS]; - int token; - int ret = 0; - - do { - p = strsep(&options, ","); - if (p != NULL && *p) { - token = match_token(p, tokens, args); - if (token == Opt_snapshot) { - if (!(sd->flags & MS_RDONLY)) { - ret++; - } else { - sd->cno = simple_strtoull(args[0].from, - NULL, 0); - /* - * No need to see the end pointer; - * match_token() has done syntax - * checking. - */ - if (sd->cno == 0) - ret++; - } - } - if (ret) - printk(KERN_ERR - "NILFS: invalid mount option: %s\n", p); - } - if (!options) - break; - BUG_ON(options == data); - *(options - 1) = ','; - } while (!ret); - return ret; -} - -static int nilfs_set_bdev_super(struct super_block *s, void *data) -{ - s->s_bdev = data; - s->s_dev = s->s_bdev->bd_dev; - return 0; -} - -static int nilfs_test_bdev_super(struct super_block *s, void *data) -{ - return (void *)s->s_bdev == data; -} - -static struct dentry * -nilfs_mount(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data) -{ - struct nilfs_super_data sd; - struct super_block *s; - fmode_t mode = FMODE_READ | FMODE_EXCL; - struct dentry *root_dentry; - int err, s_new = false; - - if (!(flags & MS_RDONLY)) - mode |= FMODE_WRITE; - - sd.bdev = blkdev_get_by_path(dev_name, mode, fs_type); - if (IS_ERR(sd.bdev)) - return ERR_CAST(sd.bdev); - - sd.cno = 0; - sd.flags = flags; - if (nilfs_identify((char *)data, &sd)) { - err = -EINVAL; - goto failed; - } - - /* - * once the super is inserted into the list by sget, s_umount - * will protect the lockfs code from trying to start a snapshot - * while we are mounting - */ - mutex_lock(&sd.bdev->bd_fsfreeze_mutex); - if (sd.bdev->bd_fsfreeze_count > 0) { - mutex_unlock(&sd.bdev->bd_fsfreeze_mutex); - err = -EBUSY; - goto failed; - } - s = sget(fs_type, nilfs_test_bdev_super, nilfs_set_bdev_super, sd.bdev); - mutex_unlock(&sd.bdev->bd_fsfreeze_mutex); - if (IS_ERR(s)) { - err = PTR_ERR(s); - goto failed; - } - - if (!s->s_root) { - char b[BDEVNAME_SIZE]; - - s_new = true; - - /* New superblock instance created */ - s->s_flags = flags; - s->s_mode = mode; - strlcpy(s->s_id, bdevname(sd.bdev, b), sizeof(s->s_id)); - sb_set_blocksize(s, block_size(sd.bdev)); - - err = nilfs_fill_super(s, data, flags & MS_SILENT ? 1 : 0); - if (err) - goto failed_super; - - s->s_flags |= MS_ACTIVE; - } else if (!sd.cno) { - int busy = false; - - if (nilfs_tree_was_touched(s->s_root)) { - busy = nilfs_try_to_shrink_tree(s->s_root); - if (busy && (flags ^ s->s_flags) & MS_RDONLY) { - printk(KERN_ERR "NILFS: the device already " - "has a %s mount.\n", - (s->s_flags & MS_RDONLY) ? - "read-only" : "read/write"); - err = -EBUSY; - goto failed_super; - } - } - if (!busy) { - /* - * Try remount to setup mount states if the current - * tree is not mounted and only snapshots use this sb. - */ - err = nilfs_remount(s, &flags, data); - if (err) - goto failed_super; - } - } - - if (sd.cno) { - err = nilfs_attach_snapshot(s, sd.cno, &root_dentry); - if (err) - goto failed_super; - } else { - root_dentry = dget(s->s_root); - } - - if (!s_new) - blkdev_put(sd.bdev, mode); - - return root_dentry; - - failed_super: - deactivate_locked_super(s); - - failed: - if (!s_new) - blkdev_put(sd.bdev, mode); - return ERR_PTR(err); -} - -struct file_system_type nilfs_fs_type = { - .owner = THIS_MODULE, - .name = "nilfs2", - .mount = nilfs_mount, - .kill_sb = kill_block_super, - .fs_flags = FS_REQUIRES_DEV, -}; - -static void nilfs_inode_init_once(void *obj) -{ - struct nilfs_inode_info *ii = obj; - - INIT_LIST_HEAD(&ii->i_dirty); -#ifdef CONFIG_NILFS_XATTR - init_rwsem(&ii->xattr_sem); -#endif - address_space_init_once(&ii->i_btnode_cache); - ii->i_bmap = &ii->i_bmap_data; - inode_init_once(&ii->vfs_inode); -} - -static void nilfs_segbuf_init_once(void *obj) -{ - memset(obj, 0, sizeof(struct nilfs_segment_buffer)); -} - -static void nilfs_destroy_cachep(void) -{ - if (nilfs_inode_cachep) - kmem_cache_destroy(nilfs_inode_cachep); - if (nilfs_transaction_cachep) - kmem_cache_destroy(nilfs_transaction_cachep); - if (nilfs_segbuf_cachep) - kmem_cache_destroy(nilfs_segbuf_cachep); - if (nilfs_btree_path_cache) - kmem_cache_destroy(nilfs_btree_path_cache); -} - -static int __init nilfs_init_cachep(void) -{ - nilfs_inode_cachep = kmem_cache_create("nilfs2_inode_cache", - sizeof(struct nilfs_inode_info), 0, - SLAB_RECLAIM_ACCOUNT, nilfs_inode_init_once); - if (!nilfs_inode_cachep) - goto fail; - - nilfs_transaction_cachep = kmem_cache_create("nilfs2_transaction_cache", - sizeof(struct nilfs_transaction_info), 0, - SLAB_RECLAIM_ACCOUNT, NULL); - if (!nilfs_transaction_cachep) - goto fail; - - nilfs_segbuf_cachep = kmem_cache_create("nilfs2_segbuf_cache", - sizeof(struct nilfs_segment_buffer), 0, - SLAB_RECLAIM_ACCOUNT, nilfs_segbuf_init_once); - if (!nilfs_segbuf_cachep) - goto fail; - - nilfs_btree_path_cache = kmem_cache_create("nilfs2_btree_path_cache", - sizeof(struct nilfs_btree_path) * NILFS_BTREE_LEVEL_MAX, - 0, 0, NULL); - if (!nilfs_btree_path_cache) - goto fail; - - return 0; - -fail: - nilfs_destroy_cachep(); - return -ENOMEM; -} - -static int __init init_nilfs_fs(void) -{ - int err; - - err = nilfs_init_cachep(); - if (err) - goto fail; - - err = register_filesystem(&nilfs_fs_type); - if (err) - goto free_cachep; - - printk(KERN_INFO "NILFS version 2 loaded\n"); - return 0; - -free_cachep: - nilfs_destroy_cachep(); -fail: - return err; -} - -static void __exit exit_nilfs_fs(void) -{ - nilfs_destroy_cachep(); - unregister_filesystem(&nilfs_fs_type); -} - -module_init(init_nilfs_fs) -module_exit(exit_nilfs_fs) diff --git a/ANDROID_3.4.5/fs/nilfs2/the_nilfs.c b/ANDROID_3.4.5/fs/nilfs2/the_nilfs.c deleted file mode 100644 index 501b7f8b..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/the_nilfs.c +++ /dev/null @@ -1,790 +0,0 @@ -/* - * the_nilfs.c - the_nilfs shared structure. - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Ryusuke Konishi <ryusuke@osrg.net> - * - */ - -#include <linux/buffer_head.h> -#include <linux/slab.h> -#include <linux/blkdev.h> -#include <linux/backing-dev.h> -#include <linux/random.h> -#include <linux/crc32.h> -#include "nilfs.h" -#include "segment.h" -#include "alloc.h" -#include "cpfile.h" -#include "sufile.h" -#include "dat.h" -#include "segbuf.h" - - -static int nilfs_valid_sb(struct nilfs_super_block *sbp); - -void nilfs_set_last_segment(struct the_nilfs *nilfs, - sector_t start_blocknr, u64 seq, __u64 cno) -{ - spin_lock(&nilfs->ns_last_segment_lock); - nilfs->ns_last_pseg = start_blocknr; - nilfs->ns_last_seq = seq; - nilfs->ns_last_cno = cno; - - if (!nilfs_sb_dirty(nilfs)) { - if (nilfs->ns_prev_seq == nilfs->ns_last_seq) - goto stay_cursor; - - set_nilfs_sb_dirty(nilfs); - } - nilfs->ns_prev_seq = nilfs->ns_last_seq; - - stay_cursor: - spin_unlock(&nilfs->ns_last_segment_lock); -} - -/** - * alloc_nilfs - allocate a nilfs object - * @bdev: block device to which the_nilfs is related - * - * Return Value: On success, pointer to the_nilfs is returned. - * On error, NULL is returned. - */ -struct the_nilfs *alloc_nilfs(struct block_device *bdev) -{ - struct the_nilfs *nilfs; - - nilfs = kzalloc(sizeof(*nilfs), GFP_KERNEL); - if (!nilfs) - return NULL; - - nilfs->ns_bdev = bdev; - atomic_set(&nilfs->ns_ndirtyblks, 0); - init_rwsem(&nilfs->ns_sem); - INIT_LIST_HEAD(&nilfs->ns_dirty_files); - INIT_LIST_HEAD(&nilfs->ns_gc_inodes); - spin_lock_init(&nilfs->ns_inode_lock); - spin_lock_init(&nilfs->ns_next_gen_lock); - spin_lock_init(&nilfs->ns_last_segment_lock); - nilfs->ns_cptree = RB_ROOT; - spin_lock_init(&nilfs->ns_cptree_lock); - init_rwsem(&nilfs->ns_segctor_sem); - - return nilfs; -} - -/** - * destroy_nilfs - destroy nilfs object - * @nilfs: nilfs object to be released - */ -void destroy_nilfs(struct the_nilfs *nilfs) -{ - might_sleep(); - if (nilfs_init(nilfs)) { - brelse(nilfs->ns_sbh[0]); - brelse(nilfs->ns_sbh[1]); - } - kfree(nilfs); -} - -static int nilfs_load_super_root(struct the_nilfs *nilfs, - struct super_block *sb, sector_t sr_block) -{ - struct buffer_head *bh_sr; - struct nilfs_super_root *raw_sr; - struct nilfs_super_block **sbp = nilfs->ns_sbp; - struct nilfs_inode *rawi; - unsigned dat_entry_size, segment_usage_size, checkpoint_size; - unsigned inode_size; - int err; - - err = nilfs_read_super_root_block(nilfs, sr_block, &bh_sr, 1); - if (unlikely(err)) - return err; - - down_read(&nilfs->ns_sem); - dat_entry_size = le16_to_cpu(sbp[0]->s_dat_entry_size); - checkpoint_size = le16_to_cpu(sbp[0]->s_checkpoint_size); - segment_usage_size = le16_to_cpu(sbp[0]->s_segment_usage_size); - up_read(&nilfs->ns_sem); - - inode_size = nilfs->ns_inode_size; - - rawi = (void *)bh_sr->b_data + NILFS_SR_DAT_OFFSET(inode_size); - err = nilfs_dat_read(sb, dat_entry_size, rawi, &nilfs->ns_dat); - if (err) - goto failed; - - rawi = (void *)bh_sr->b_data + NILFS_SR_CPFILE_OFFSET(inode_size); - err = nilfs_cpfile_read(sb, checkpoint_size, rawi, &nilfs->ns_cpfile); - if (err) - goto failed_dat; - - rawi = (void *)bh_sr->b_data + NILFS_SR_SUFILE_OFFSET(inode_size); - err = nilfs_sufile_read(sb, segment_usage_size, rawi, - &nilfs->ns_sufile); - if (err) - goto failed_cpfile; - - raw_sr = (struct nilfs_super_root *)bh_sr->b_data; - nilfs->ns_nongc_ctime = le64_to_cpu(raw_sr->sr_nongc_ctime); - - failed: - brelse(bh_sr); - return err; - - failed_cpfile: - iput(nilfs->ns_cpfile); - - failed_dat: - iput(nilfs->ns_dat); - goto failed; -} - -static void nilfs_init_recovery_info(struct nilfs_recovery_info *ri) -{ - memset(ri, 0, sizeof(*ri)); - INIT_LIST_HEAD(&ri->ri_used_segments); -} - -static void nilfs_clear_recovery_info(struct nilfs_recovery_info *ri) -{ - nilfs_dispose_segment_list(&ri->ri_used_segments); -} - -/** - * nilfs_store_log_cursor - load log cursor from a super block - * @nilfs: nilfs object - * @sbp: buffer storing super block to be read - * - * nilfs_store_log_cursor() reads the last position of the log - * containing a super root from a given super block, and initializes - * relevant information on the nilfs object preparatory for log - * scanning and recovery. - */ -static int nilfs_store_log_cursor(struct the_nilfs *nilfs, - struct nilfs_super_block *sbp) -{ - int ret = 0; - - nilfs->ns_last_pseg = le64_to_cpu(sbp->s_last_pseg); - nilfs->ns_last_cno = le64_to_cpu(sbp->s_last_cno); - nilfs->ns_last_seq = le64_to_cpu(sbp->s_last_seq); - - nilfs->ns_prev_seq = nilfs->ns_last_seq; - nilfs->ns_seg_seq = nilfs->ns_last_seq; - nilfs->ns_segnum = - nilfs_get_segnum_of_block(nilfs, nilfs->ns_last_pseg); - nilfs->ns_cno = nilfs->ns_last_cno + 1; - if (nilfs->ns_segnum >= nilfs->ns_nsegments) { - printk(KERN_ERR "NILFS invalid last segment number.\n"); - ret = -EINVAL; - } - return ret; -} - -/** - * load_nilfs - load and recover the nilfs - * @nilfs: the_nilfs structure to be released - * @sb: super block isntance used to recover past segment - * - * load_nilfs() searches and load the latest super root, - * attaches the last segment, and does recovery if needed. - * The caller must call this exclusively for simultaneous mounts. - */ -int load_nilfs(struct the_nilfs *nilfs, struct super_block *sb) -{ - struct nilfs_recovery_info ri; - unsigned int s_flags = sb->s_flags; - int really_read_only = bdev_read_only(nilfs->ns_bdev); - int valid_fs = nilfs_valid_fs(nilfs); - int err; - - if (!valid_fs) { - printk(KERN_WARNING "NILFS warning: mounting unchecked fs\n"); - if (s_flags & MS_RDONLY) { - printk(KERN_INFO "NILFS: INFO: recovery " - "required for readonly filesystem.\n"); - printk(KERN_INFO "NILFS: write access will " - "be enabled during recovery.\n"); - } - } - - nilfs_init_recovery_info(&ri); - - err = nilfs_search_super_root(nilfs, &ri); - if (unlikely(err)) { - struct nilfs_super_block **sbp = nilfs->ns_sbp; - int blocksize; - - if (err != -EINVAL) - goto scan_error; - - if (!nilfs_valid_sb(sbp[1])) { - printk(KERN_WARNING - "NILFS warning: unable to fall back to spare" - "super block\n"); - goto scan_error; - } - printk(KERN_INFO - "NILFS: try rollback from an earlier position\n"); - - /* - * restore super block with its spare and reconfigure - * relevant states of the nilfs object. - */ - memcpy(sbp[0], sbp[1], nilfs->ns_sbsize); - nilfs->ns_crc_seed = le32_to_cpu(sbp[0]->s_crc_seed); - nilfs->ns_sbwtime = le64_to_cpu(sbp[0]->s_wtime); - - /* verify consistency between two super blocks */ - blocksize = BLOCK_SIZE << le32_to_cpu(sbp[0]->s_log_block_size); - if (blocksize != nilfs->ns_blocksize) { - printk(KERN_WARNING - "NILFS warning: blocksize differs between " - "two super blocks (%d != %d)\n", - blocksize, nilfs->ns_blocksize); - goto scan_error; - } - - err = nilfs_store_log_cursor(nilfs, sbp[0]); - if (err) - goto scan_error; - - /* drop clean flag to allow roll-forward and recovery */ - nilfs->ns_mount_state &= ~NILFS_VALID_FS; - valid_fs = 0; - - err = nilfs_search_super_root(nilfs, &ri); - if (err) - goto scan_error; - } - - err = nilfs_load_super_root(nilfs, sb, ri.ri_super_root); - if (unlikely(err)) { - printk(KERN_ERR "NILFS: error loading super root.\n"); - goto failed; - } - - if (valid_fs) - goto skip_recovery; - - if (s_flags & MS_RDONLY) { - __u64 features; - - if (nilfs_test_opt(nilfs, NORECOVERY)) { - printk(KERN_INFO "NILFS: norecovery option specified. " - "skipping roll-forward recovery\n"); - goto skip_recovery; - } - features = le64_to_cpu(nilfs->ns_sbp[0]->s_feature_compat_ro) & - ~NILFS_FEATURE_COMPAT_RO_SUPP; - if (features) { - printk(KERN_ERR "NILFS: couldn't proceed with " - "recovery because of unsupported optional " - "features (%llx)\n", - (unsigned long long)features); - err = -EROFS; - goto failed_unload; - } - if (really_read_only) { - printk(KERN_ERR "NILFS: write access " - "unavailable, cannot proceed.\n"); - err = -EROFS; - goto failed_unload; - } - sb->s_flags &= ~MS_RDONLY; - } else if (nilfs_test_opt(nilfs, NORECOVERY)) { - printk(KERN_ERR "NILFS: recovery cancelled because norecovery " - "option was specified for a read/write mount\n"); - err = -EINVAL; - goto failed_unload; - } - - err = nilfs_salvage_orphan_logs(nilfs, sb, &ri); - if (err) - goto failed_unload; - - down_write(&nilfs->ns_sem); - nilfs->ns_mount_state |= NILFS_VALID_FS; /* set "clean" flag */ - err = nilfs_cleanup_super(sb); - up_write(&nilfs->ns_sem); - - if (err) { - printk(KERN_ERR "NILFS: failed to update super block. " - "recovery unfinished.\n"); - goto failed_unload; - } - printk(KERN_INFO "NILFS: recovery complete.\n"); - - skip_recovery: - nilfs_clear_recovery_info(&ri); - sb->s_flags = s_flags; - return 0; - - scan_error: - printk(KERN_ERR "NILFS: error searching super root.\n"); - goto failed; - - failed_unload: - iput(nilfs->ns_cpfile); - iput(nilfs->ns_sufile); - iput(nilfs->ns_dat); - - failed: - nilfs_clear_recovery_info(&ri); - sb->s_flags = s_flags; - return err; -} - -static unsigned long long nilfs_max_size(unsigned int blkbits) -{ - unsigned int max_bits; - unsigned long long res = MAX_LFS_FILESIZE; /* page cache limit */ - - max_bits = blkbits + NILFS_BMAP_KEY_BIT; /* bmap size limit */ - if (max_bits < 64) - res = min_t(unsigned long long, res, (1ULL << max_bits) - 1); - return res; -} - -/** - * nilfs_nrsvsegs - calculate the number of reserved segments - * @nilfs: nilfs object - * @nsegs: total number of segments - */ -unsigned long nilfs_nrsvsegs(struct the_nilfs *nilfs, unsigned long nsegs) -{ - return max_t(unsigned long, NILFS_MIN_NRSVSEGS, - DIV_ROUND_UP(nsegs * nilfs->ns_r_segments_percentage, - 100)); -} - -void nilfs_set_nsegments(struct the_nilfs *nilfs, unsigned long nsegs) -{ - nilfs->ns_nsegments = nsegs; - nilfs->ns_nrsvsegs = nilfs_nrsvsegs(nilfs, nsegs); -} - -static int nilfs_store_disk_layout(struct the_nilfs *nilfs, - struct nilfs_super_block *sbp) -{ - if (le32_to_cpu(sbp->s_rev_level) < NILFS_MIN_SUPP_REV) { - printk(KERN_ERR "NILFS: unsupported revision " - "(superblock rev.=%d.%d, current rev.=%d.%d). " - "Please check the version of mkfs.nilfs.\n", - le32_to_cpu(sbp->s_rev_level), - le16_to_cpu(sbp->s_minor_rev_level), - NILFS_CURRENT_REV, NILFS_MINOR_REV); - return -EINVAL; - } - nilfs->ns_sbsize = le16_to_cpu(sbp->s_bytes); - if (nilfs->ns_sbsize > BLOCK_SIZE) - return -EINVAL; - - nilfs->ns_inode_size = le16_to_cpu(sbp->s_inode_size); - nilfs->ns_first_ino = le32_to_cpu(sbp->s_first_ino); - - nilfs->ns_blocks_per_segment = le32_to_cpu(sbp->s_blocks_per_segment); - if (nilfs->ns_blocks_per_segment < NILFS_SEG_MIN_BLOCKS) { - printk(KERN_ERR "NILFS: too short segment.\n"); - return -EINVAL; - } - - nilfs->ns_first_data_block = le64_to_cpu(sbp->s_first_data_block); - nilfs->ns_r_segments_percentage = - le32_to_cpu(sbp->s_r_segments_percentage); - if (nilfs->ns_r_segments_percentage < 1 || - nilfs->ns_r_segments_percentage > 99) { - printk(KERN_ERR "NILFS: invalid reserved segments percentage.\n"); - return -EINVAL; - } - - nilfs_set_nsegments(nilfs, le64_to_cpu(sbp->s_nsegments)); - nilfs->ns_crc_seed = le32_to_cpu(sbp->s_crc_seed); - return 0; -} - -static int nilfs_valid_sb(struct nilfs_super_block *sbp) -{ - static unsigned char sum[4]; - const int sumoff = offsetof(struct nilfs_super_block, s_sum); - size_t bytes; - u32 crc; - - if (!sbp || le16_to_cpu(sbp->s_magic) != NILFS_SUPER_MAGIC) - return 0; - bytes = le16_to_cpu(sbp->s_bytes); - if (bytes > BLOCK_SIZE) - return 0; - crc = crc32_le(le32_to_cpu(sbp->s_crc_seed), (unsigned char *)sbp, - sumoff); - crc = crc32_le(crc, sum, 4); - crc = crc32_le(crc, (unsigned char *)sbp + sumoff + 4, - bytes - sumoff - 4); - return crc == le32_to_cpu(sbp->s_sum); -} - -static int nilfs_sb2_bad_offset(struct nilfs_super_block *sbp, u64 offset) -{ - return offset < ((le64_to_cpu(sbp->s_nsegments) * - le32_to_cpu(sbp->s_blocks_per_segment)) << - (le32_to_cpu(sbp->s_log_block_size) + 10)); -} - -static void nilfs_release_super_block(struct the_nilfs *nilfs) -{ - int i; - - for (i = 0; i < 2; i++) { - if (nilfs->ns_sbp[i]) { - brelse(nilfs->ns_sbh[i]); - nilfs->ns_sbh[i] = NULL; - nilfs->ns_sbp[i] = NULL; - } - } -} - -void nilfs_fall_back_super_block(struct the_nilfs *nilfs) -{ - brelse(nilfs->ns_sbh[0]); - nilfs->ns_sbh[0] = nilfs->ns_sbh[1]; - nilfs->ns_sbp[0] = nilfs->ns_sbp[1]; - nilfs->ns_sbh[1] = NULL; - nilfs->ns_sbp[1] = NULL; -} - -void nilfs_swap_super_block(struct the_nilfs *nilfs) -{ - struct buffer_head *tsbh = nilfs->ns_sbh[0]; - struct nilfs_super_block *tsbp = nilfs->ns_sbp[0]; - - nilfs->ns_sbh[0] = nilfs->ns_sbh[1]; - nilfs->ns_sbp[0] = nilfs->ns_sbp[1]; - nilfs->ns_sbh[1] = tsbh; - nilfs->ns_sbp[1] = tsbp; -} - -static int nilfs_load_super_block(struct the_nilfs *nilfs, - struct super_block *sb, int blocksize, - struct nilfs_super_block **sbpp) -{ - struct nilfs_super_block **sbp = nilfs->ns_sbp; - struct buffer_head **sbh = nilfs->ns_sbh; - u64 sb2off = NILFS_SB2_OFFSET_BYTES(nilfs->ns_bdev->bd_inode->i_size); - int valid[2], swp = 0; - - sbp[0] = nilfs_read_super_block(sb, NILFS_SB_OFFSET_BYTES, blocksize, - &sbh[0]); - sbp[1] = nilfs_read_super_block(sb, sb2off, blocksize, &sbh[1]); - - if (!sbp[0]) { - if (!sbp[1]) { - printk(KERN_ERR "NILFS: unable to read superblock\n"); - return -EIO; - } - printk(KERN_WARNING - "NILFS warning: unable to read primary superblock " - "(blocksize = %d)\n", blocksize); - } else if (!sbp[1]) { - printk(KERN_WARNING - "NILFS warning: unable to read secondary superblock " - "(blocksize = %d)\n", blocksize); - } - - /* - * Compare two super blocks and set 1 in swp if the secondary - * super block is valid and newer. Otherwise, set 0 in swp. - */ - valid[0] = nilfs_valid_sb(sbp[0]); - valid[1] = nilfs_valid_sb(sbp[1]); - swp = valid[1] && (!valid[0] || - le64_to_cpu(sbp[1]->s_last_cno) > - le64_to_cpu(sbp[0]->s_last_cno)); - - if (valid[swp] && nilfs_sb2_bad_offset(sbp[swp], sb2off)) { - brelse(sbh[1]); - sbh[1] = NULL; - sbp[1] = NULL; - valid[1] = 0; - swp = 0; - } - if (!valid[swp]) { - nilfs_release_super_block(nilfs); - printk(KERN_ERR "NILFS: Can't find nilfs on dev %s.\n", - sb->s_id); - return -EINVAL; - } - - if (!valid[!swp]) - printk(KERN_WARNING "NILFS warning: broken superblock. " - "using spare superblock (blocksize = %d).\n", blocksize); - if (swp) - nilfs_swap_super_block(nilfs); - - nilfs->ns_sbwcount = 0; - nilfs->ns_sbwtime = le64_to_cpu(sbp[0]->s_wtime); - nilfs->ns_prot_seq = le64_to_cpu(sbp[valid[1] & !swp]->s_last_seq); - *sbpp = sbp[0]; - return 0; -} - -/** - * init_nilfs - initialize a NILFS instance. - * @nilfs: the_nilfs structure - * @sb: super block - * @data: mount options - * - * init_nilfs() performs common initialization per block device (e.g. - * reading the super block, getting disk layout information, initializing - * shared fields in the_nilfs). - * - * Return Value: On success, 0 is returned. On error, a negative error - * code is returned. - */ -int init_nilfs(struct the_nilfs *nilfs, struct super_block *sb, char *data) -{ - struct nilfs_super_block *sbp; - int blocksize; - int err; - - down_write(&nilfs->ns_sem); - - blocksize = sb_min_blocksize(sb, NILFS_MIN_BLOCK_SIZE); - if (!blocksize) { - printk(KERN_ERR "NILFS: unable to set blocksize\n"); - err = -EINVAL; - goto out; - } - err = nilfs_load_super_block(nilfs, sb, blocksize, &sbp); - if (err) - goto out; - - err = nilfs_store_magic_and_option(sb, sbp, data); - if (err) - goto failed_sbh; - - err = nilfs_check_feature_compatibility(sb, sbp); - if (err) - goto failed_sbh; - - blocksize = BLOCK_SIZE << le32_to_cpu(sbp->s_log_block_size); - if (blocksize < NILFS_MIN_BLOCK_SIZE || - blocksize > NILFS_MAX_BLOCK_SIZE) { - printk(KERN_ERR "NILFS: couldn't mount because of unsupported " - "filesystem blocksize %d\n", blocksize); - err = -EINVAL; - goto failed_sbh; - } - if (sb->s_blocksize != blocksize) { - int hw_blocksize = bdev_logical_block_size(sb->s_bdev); - - if (blocksize < hw_blocksize) { - printk(KERN_ERR - "NILFS: blocksize %d too small for device " - "(sector-size = %d).\n", - blocksize, hw_blocksize); - err = -EINVAL; - goto failed_sbh; - } - nilfs_release_super_block(nilfs); - sb_set_blocksize(sb, blocksize); - - err = nilfs_load_super_block(nilfs, sb, blocksize, &sbp); - if (err) - goto out; - /* not failed_sbh; sbh is released automatically - when reloading fails. */ - } - nilfs->ns_blocksize_bits = sb->s_blocksize_bits; - nilfs->ns_blocksize = blocksize; - - get_random_bytes(&nilfs->ns_next_generation, - sizeof(nilfs->ns_next_generation)); - - err = nilfs_store_disk_layout(nilfs, sbp); - if (err) - goto failed_sbh; - - sb->s_maxbytes = nilfs_max_size(sb->s_blocksize_bits); - - nilfs->ns_mount_state = le16_to_cpu(sbp->s_state); - - err = nilfs_store_log_cursor(nilfs, sbp); - if (err) - goto failed_sbh; - - set_nilfs_init(nilfs); - err = 0; - out: - up_write(&nilfs->ns_sem); - return err; - - failed_sbh: - nilfs_release_super_block(nilfs); - goto out; -} - -int nilfs_discard_segments(struct the_nilfs *nilfs, __u64 *segnump, - size_t nsegs) -{ - sector_t seg_start, seg_end; - sector_t start = 0, nblocks = 0; - unsigned int sects_per_block; - __u64 *sn; - int ret = 0; - - sects_per_block = (1 << nilfs->ns_blocksize_bits) / - bdev_logical_block_size(nilfs->ns_bdev); - for (sn = segnump; sn < segnump + nsegs; sn++) { - nilfs_get_segment_range(nilfs, *sn, &seg_start, &seg_end); - - if (!nblocks) { - start = seg_start; - nblocks = seg_end - seg_start + 1; - } else if (start + nblocks == seg_start) { - nblocks += seg_end - seg_start + 1; - } else { - ret = blkdev_issue_discard(nilfs->ns_bdev, - start * sects_per_block, - nblocks * sects_per_block, - GFP_NOFS, 0); - if (ret < 0) - return ret; - nblocks = 0; - } - } - if (nblocks) - ret = blkdev_issue_discard(nilfs->ns_bdev, - start * sects_per_block, - nblocks * sects_per_block, - GFP_NOFS, 0); - return ret; -} - -int nilfs_count_free_blocks(struct the_nilfs *nilfs, sector_t *nblocks) -{ - unsigned long ncleansegs; - - down_read(&NILFS_MDT(nilfs->ns_dat)->mi_sem); - ncleansegs = nilfs_sufile_get_ncleansegs(nilfs->ns_sufile); - up_read(&NILFS_MDT(nilfs->ns_dat)->mi_sem); - *nblocks = (sector_t)ncleansegs * nilfs->ns_blocks_per_segment; - return 0; -} - -int nilfs_near_disk_full(struct the_nilfs *nilfs) -{ - unsigned long ncleansegs, nincsegs; - - ncleansegs = nilfs_sufile_get_ncleansegs(nilfs->ns_sufile); - nincsegs = atomic_read(&nilfs->ns_ndirtyblks) / - nilfs->ns_blocks_per_segment + 1; - - return ncleansegs <= nilfs->ns_nrsvsegs + nincsegs; -} - -struct nilfs_root *nilfs_lookup_root(struct the_nilfs *nilfs, __u64 cno) -{ - struct rb_node *n; - struct nilfs_root *root; - - spin_lock(&nilfs->ns_cptree_lock); - n = nilfs->ns_cptree.rb_node; - while (n) { - root = rb_entry(n, struct nilfs_root, rb_node); - - if (cno < root->cno) { - n = n->rb_left; - } else if (cno > root->cno) { - n = n->rb_right; - } else { - atomic_inc(&root->count); - spin_unlock(&nilfs->ns_cptree_lock); - return root; - } - } - spin_unlock(&nilfs->ns_cptree_lock); - - return NULL; -} - -struct nilfs_root * -nilfs_find_or_create_root(struct the_nilfs *nilfs, __u64 cno) -{ - struct rb_node **p, *parent; - struct nilfs_root *root, *new; - - root = nilfs_lookup_root(nilfs, cno); - if (root) - return root; - - new = kmalloc(sizeof(*root), GFP_KERNEL); - if (!new) - return NULL; - - spin_lock(&nilfs->ns_cptree_lock); - - p = &nilfs->ns_cptree.rb_node; - parent = NULL; - - while (*p) { - parent = *p; - root = rb_entry(parent, struct nilfs_root, rb_node); - - if (cno < root->cno) { - p = &(*p)->rb_left; - } else if (cno > root->cno) { - p = &(*p)->rb_right; - } else { - atomic_inc(&root->count); - spin_unlock(&nilfs->ns_cptree_lock); - kfree(new); - return root; - } - } - - new->cno = cno; - new->ifile = NULL; - new->nilfs = nilfs; - atomic_set(&new->count, 1); - atomic_set(&new->inodes_count, 0); - atomic_set(&new->blocks_count, 0); - - rb_link_node(&new->rb_node, parent, p); - rb_insert_color(&new->rb_node, &nilfs->ns_cptree); - - spin_unlock(&nilfs->ns_cptree_lock); - - return new; -} - -void nilfs_put_root(struct nilfs_root *root) -{ - if (atomic_dec_and_test(&root->count)) { - struct the_nilfs *nilfs = root->nilfs; - - spin_lock(&nilfs->ns_cptree_lock); - rb_erase(&root->rb_node, &nilfs->ns_cptree); - spin_unlock(&nilfs->ns_cptree_lock); - if (root->ifile) - iput(root->ifile); - - kfree(root); - } -} diff --git a/ANDROID_3.4.5/fs/nilfs2/the_nilfs.h b/ANDROID_3.4.5/fs/nilfs2/the_nilfs.h deleted file mode 100644 index 9992b113..00000000 --- a/ANDROID_3.4.5/fs/nilfs2/the_nilfs.h +++ /dev/null @@ -1,356 +0,0 @@ -/* - * the_nilfs.h - the_nilfs shared structure. - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Ryusuke Konishi <ryusuke@osrg.net> - * - */ - -#ifndef _THE_NILFS_H -#define _THE_NILFS_H - -#include <linux/types.h> -#include <linux/buffer_head.h> -#include <linux/rbtree.h> -#include <linux/fs.h> -#include <linux/blkdev.h> -#include <linux/backing-dev.h> -#include <linux/slab.h> - -struct nilfs_sc_info; - -/* the_nilfs struct */ -enum { - THE_NILFS_INIT = 0, /* Information from super_block is set */ - THE_NILFS_DISCONTINUED, /* 'next' pointer chain has broken */ - THE_NILFS_GC_RUNNING, /* gc process is running */ - THE_NILFS_SB_DIRTY, /* super block is dirty */ -}; - -/** - * struct the_nilfs - struct to supervise multiple nilfs mount points - * @ns_flags: flags - * @ns_bdev: block device - * @ns_sem: semaphore for shared states - * @ns_sbh: buffer heads of on-disk super blocks - * @ns_sbp: pointers to super block data - * @ns_sbwtime: previous write time of super block - * @ns_sbwcount: write count of super block - * @ns_sbsize: size of valid data in super block - * @ns_seg_seq: segment sequence counter - * @ns_segnum: index number of the latest full segment. - * @ns_nextnum: index number of the full segment index to be used next - * @ns_pseg_offset: offset of next partial segment in the current full segment - * @ns_cno: next checkpoint number - * @ns_ctime: write time of the last segment - * @ns_nongc_ctime: write time of the last segment not for cleaner operation - * @ns_ndirtyblks: Number of dirty data blocks - * @ns_last_segment_lock: lock protecting fields for the latest segment - * @ns_last_pseg: start block number of the latest segment - * @ns_last_seq: sequence value of the latest segment - * @ns_last_cno: checkpoint number of the latest segment - * @ns_prot_seq: least sequence number of segments which must not be reclaimed - * @ns_prev_seq: base sequence number used to decide if advance log cursor - * @ns_writer: log writer - * @ns_segctor_sem: semaphore protecting log write - * @ns_dat: DAT file inode - * @ns_cpfile: checkpoint file inode - * @ns_sufile: segusage file inode - * @ns_cptree: rb-tree of all mounted checkpoints (nilfs_root) - * @ns_cptree_lock: lock protecting @ns_cptree - * @ns_dirty_files: list of dirty files - * @ns_inode_lock: lock protecting @ns_dirty_files - * @ns_gc_inodes: dummy inodes to keep live blocks - * @ns_next_generation: next generation number for inodes - * @ns_next_gen_lock: lock protecting @ns_next_generation - * @ns_mount_opt: mount options - * @ns_resuid: uid for reserved blocks - * @ns_resgid: gid for reserved blocks - * @ns_interval: checkpoint creation interval - * @ns_watermark: watermark for the number of dirty buffers - * @ns_blocksize_bits: bit length of block size - * @ns_blocksize: block size - * @ns_nsegments: number of segments in filesystem - * @ns_blocks_per_segment: number of blocks per segment - * @ns_r_segments_percentage: reserved segments percentage - * @ns_nrsvsegs: number of reserved segments - * @ns_first_data_block: block number of first data block - * @ns_inode_size: size of on-disk inode - * @ns_first_ino: first not-special inode number - * @ns_crc_seed: seed value of CRC32 calculation - */ -struct the_nilfs { - unsigned long ns_flags; - - struct block_device *ns_bdev; - struct rw_semaphore ns_sem; - - /* - * used for - * - loading the latest checkpoint exclusively. - * - allocating a new full segment. - * - protecting s_dirt in the super_block struct - * (see nilfs_write_super) and the following fields. - */ - struct buffer_head *ns_sbh[2]; - struct nilfs_super_block *ns_sbp[2]; - time_t ns_sbwtime; - unsigned ns_sbwcount; - unsigned ns_sbsize; - unsigned ns_mount_state; - - /* - * Following fields are dedicated to a writable FS-instance. - * Except for the period seeking checkpoint, code outside the segment - * constructor must lock a segment semaphore while accessing these - * fields. - * The writable FS-instance is sole during a lifetime of the_nilfs. - */ - u64 ns_seg_seq; - __u64 ns_segnum; - __u64 ns_nextnum; - unsigned long ns_pseg_offset; - __u64 ns_cno; - time_t ns_ctime; - time_t ns_nongc_ctime; - atomic_t ns_ndirtyblks; - - /* - * The following fields hold information on the latest partial segment - * written to disk with a super root. These fields are protected by - * ns_last_segment_lock. - */ - spinlock_t ns_last_segment_lock; - sector_t ns_last_pseg; - u64 ns_last_seq; - __u64 ns_last_cno; - u64 ns_prot_seq; - u64 ns_prev_seq; - - struct nilfs_sc_info *ns_writer; - struct rw_semaphore ns_segctor_sem; - - /* - * Following fields are lock free except for the period before - * the_nilfs is initialized. - */ - struct inode *ns_dat; - struct inode *ns_cpfile; - struct inode *ns_sufile; - - /* Checkpoint tree */ - struct rb_root ns_cptree; - spinlock_t ns_cptree_lock; - - /* Dirty inode list */ - struct list_head ns_dirty_files; - spinlock_t ns_inode_lock; - - /* GC inode list */ - struct list_head ns_gc_inodes; - - /* Inode allocator */ - u32 ns_next_generation; - spinlock_t ns_next_gen_lock; - - /* Mount options */ - unsigned long ns_mount_opt; - - uid_t ns_resuid; - gid_t ns_resgid; - unsigned long ns_interval; - unsigned long ns_watermark; - - /* Disk layout information (static) */ - unsigned int ns_blocksize_bits; - unsigned int ns_blocksize; - unsigned long ns_nsegments; - unsigned long ns_blocks_per_segment; - unsigned long ns_r_segments_percentage; - unsigned long ns_nrsvsegs; - unsigned long ns_first_data_block; - int ns_inode_size; - int ns_first_ino; - u32 ns_crc_seed; -}; - -#define THE_NILFS_FNS(bit, name) \ -static inline void set_nilfs_##name(struct the_nilfs *nilfs) \ -{ \ - set_bit(THE_NILFS_##bit, &(nilfs)->ns_flags); \ -} \ -static inline void clear_nilfs_##name(struct the_nilfs *nilfs) \ -{ \ - clear_bit(THE_NILFS_##bit, &(nilfs)->ns_flags); \ -} \ -static inline int nilfs_##name(struct the_nilfs *nilfs) \ -{ \ - return test_bit(THE_NILFS_##bit, &(nilfs)->ns_flags); \ -} - -THE_NILFS_FNS(INIT, init) -THE_NILFS_FNS(DISCONTINUED, discontinued) -THE_NILFS_FNS(GC_RUNNING, gc_running) -THE_NILFS_FNS(SB_DIRTY, sb_dirty) - -/* - * Mount option operations - */ -#define nilfs_clear_opt(nilfs, opt) \ - do { (nilfs)->ns_mount_opt &= ~NILFS_MOUNT_##opt; } while (0) -#define nilfs_set_opt(nilfs, opt) \ - do { (nilfs)->ns_mount_opt |= NILFS_MOUNT_##opt; } while (0) -#define nilfs_test_opt(nilfs, opt) ((nilfs)->ns_mount_opt & NILFS_MOUNT_##opt) -#define nilfs_write_opt(nilfs, mask, opt) \ - do { (nilfs)->ns_mount_opt = \ - (((nilfs)->ns_mount_opt & ~NILFS_MOUNT_##mask) | \ - NILFS_MOUNT_##opt); \ - } while (0) - -/** - * struct nilfs_root - nilfs root object - * @cno: checkpoint number - * @rb_node: red-black tree node - * @count: refcount of this structure - * @nilfs: nilfs object - * @ifile: inode file - * @root: root inode - * @inodes_count: number of inodes - * @blocks_count: number of blocks (Reserved) - */ -struct nilfs_root { - __u64 cno; - struct rb_node rb_node; - - atomic_t count; - struct the_nilfs *nilfs; - struct inode *ifile; - - atomic_t inodes_count; - atomic_t blocks_count; -}; - -/* Special checkpoint number */ -#define NILFS_CPTREE_CURRENT_CNO 0 - -/* Minimum interval of periodical update of superblocks (in seconds) */ -#define NILFS_SB_FREQ 10 - -static inline int nilfs_sb_need_update(struct the_nilfs *nilfs) -{ - u64 t = get_seconds(); - return t < nilfs->ns_sbwtime || t > nilfs->ns_sbwtime + NILFS_SB_FREQ; -} - -static inline int nilfs_sb_will_flip(struct the_nilfs *nilfs) -{ - int flip_bits = nilfs->ns_sbwcount & 0x0FL; - return (flip_bits != 0x08 && flip_bits != 0x0F); -} - -void nilfs_set_last_segment(struct the_nilfs *, sector_t, u64, __u64); -struct the_nilfs *alloc_nilfs(struct block_device *bdev); -void destroy_nilfs(struct the_nilfs *nilfs); -int init_nilfs(struct the_nilfs *nilfs, struct super_block *sb, char *data); -int load_nilfs(struct the_nilfs *nilfs, struct super_block *sb); -unsigned long nilfs_nrsvsegs(struct the_nilfs *nilfs, unsigned long nsegs); -void nilfs_set_nsegments(struct the_nilfs *nilfs, unsigned long nsegs); -int nilfs_discard_segments(struct the_nilfs *, __u64 *, size_t); -int nilfs_count_free_blocks(struct the_nilfs *, sector_t *); -struct nilfs_root *nilfs_lookup_root(struct the_nilfs *nilfs, __u64 cno); -struct nilfs_root *nilfs_find_or_create_root(struct the_nilfs *nilfs, - __u64 cno); -void nilfs_put_root(struct nilfs_root *root); -int nilfs_near_disk_full(struct the_nilfs *); -void nilfs_fall_back_super_block(struct the_nilfs *); -void nilfs_swap_super_block(struct the_nilfs *); - - -static inline void nilfs_get_root(struct nilfs_root *root) -{ - atomic_inc(&root->count); -} - -static inline int nilfs_valid_fs(struct the_nilfs *nilfs) -{ - unsigned valid_fs; - - down_read(&nilfs->ns_sem); - valid_fs = (nilfs->ns_mount_state & NILFS_VALID_FS); - up_read(&nilfs->ns_sem); - return valid_fs; -} - -static inline void -nilfs_get_segment_range(struct the_nilfs *nilfs, __u64 segnum, - sector_t *seg_start, sector_t *seg_end) -{ - *seg_start = (sector_t)nilfs->ns_blocks_per_segment * segnum; - *seg_end = *seg_start + nilfs->ns_blocks_per_segment - 1; - if (segnum == 0) - *seg_start = nilfs->ns_first_data_block; -} - -static inline sector_t -nilfs_get_segment_start_blocknr(struct the_nilfs *nilfs, __u64 segnum) -{ - return (segnum == 0) ? nilfs->ns_first_data_block : - (sector_t)nilfs->ns_blocks_per_segment * segnum; -} - -static inline __u64 -nilfs_get_segnum_of_block(struct the_nilfs *nilfs, sector_t blocknr) -{ - sector_t segnum = blocknr; - - sector_div(segnum, nilfs->ns_blocks_per_segment); - return segnum; -} - -static inline void -nilfs_terminate_segment(struct the_nilfs *nilfs, sector_t seg_start, - sector_t seg_end) -{ - /* terminate the current full segment (used in case of I/O-error) */ - nilfs->ns_pseg_offset = seg_end - seg_start + 1; -} - -static inline void nilfs_shift_to_next_segment(struct the_nilfs *nilfs) -{ - /* move forward with a full segment */ - nilfs->ns_segnum = nilfs->ns_nextnum; - nilfs->ns_pseg_offset = 0; - nilfs->ns_seg_seq++; -} - -static inline __u64 nilfs_last_cno(struct the_nilfs *nilfs) -{ - __u64 cno; - - spin_lock(&nilfs->ns_last_segment_lock); - cno = nilfs->ns_last_cno; - spin_unlock(&nilfs->ns_last_segment_lock); - return cno; -} - -static inline int nilfs_segment_is_active(struct the_nilfs *nilfs, __u64 n) -{ - return n == nilfs->ns_segnum || n == nilfs->ns_nextnum; -} - -#endif /* _THE_NILFS_H */ |