Marco Stornellilogfs: drop vmtruncate5dfc28213 years agoHistory
2299 lines · 55.8 KB · C
RawDownload
/* * fs/logfs/readwrite.c * * As should be obvious for Linux kernel code, license is GPLv2 * * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org> * * * Actually contains five sets of very similar functions: * read		read blocks from a file * seek_hole	find next hole * seek_data	find next data block * valid	check whether a block still belongs to a file * write	write blocks to a file * delete	delete a block (for directories and ifile) * rewrite	move existing blocks of a file to a new location (gc helper) * truncate	truncate a file */#include "logfs.h"#include <linux/sched.h>#include <linux/slab.h> static u64 adjust_bix(u64 bix, level_t level){	switch (level) {	case 0:		return bix;	case LEVEL(1):		return max_t(u64, bix, I0_BLOCKS);	case LEVEL(2):		return max_t(u64, bix, I1_BLOCKS);	case LEVEL(3):		return max_t(u64, bix, I2_BLOCKS);	case LEVEL(4):		return max_t(u64, bix, I3_BLOCKS);	case LEVEL(5):		return max_t(u64, bix, I4_BLOCKS);	default:		WARN_ON(1);		return bix;	}} static inline u64 maxbix(u8 height){	return 1ULL << (LOGFS_BLOCK_BITS * height);} /** * The inode address space is cut in two halves.  Lower half belongs to data * pages, upper half to indirect blocks.  If the high bit (INDIRECT_BIT) is * set, the actual block index (bix) and level can be derived from the page * index. * * The lowest three bits of the block index are set to 0 after packing and * unpacking.  Since the lowest n bits (9 for 4KiB blocksize) are ignored * anyway this is harmless. */#define ARCH_SHIFT	(BITS_PER_LONG - 32)#define INDIRECT_BIT	(0x80000000UL << ARCH_SHIFT)#define LEVEL_SHIFT	(28 + ARCH_SHIFT)static inline pgoff_t first_indirect_block(void){	return INDIRECT_BIT | (1ULL << LEVEL_SHIFT);} pgoff_t logfs_pack_index(u64 bix, level_t level){	pgoff_t index; 	BUG_ON(bix >= INDIRECT_BIT);	if (level == 0)		return bix; 	index  = INDIRECT_BIT;	index |= (__force long)level << LEVEL_SHIFT;	index |= bix >> ((__force u8)level * LOGFS_BLOCK_BITS);	return index;} void logfs_unpack_index(pgoff_t index, u64 *bix, level_t *level){	u8 __level; 	if (!(index & INDIRECT_BIT)) {		*bix = index;		*level = 0;		return;	} 	__level = (index & ~INDIRECT_BIT) >> LEVEL_SHIFT;	*level = LEVEL(__level);	*bix = (index << (__level * LOGFS_BLOCK_BITS)) & ~INDIRECT_BIT;	*bix = adjust_bix(*bix, *level);	return;}#undef ARCH_SHIFT#undef INDIRECT_BIT#undef LEVEL_SHIFT /* * Time is stored as nanoseconds since the epoch. */static struct timespec be64_to_timespec(__be64 betime){	return ns_to_timespec(be64_to_cpu(betime));} static __be64 timespec_to_be64(struct timespec tsp){	return cpu_to_be64((u64)tsp.tv_sec * NSEC_PER_SEC + tsp.tv_nsec);} static void logfs_disk_to_inode(struct logfs_disk_inode *di, struct inode*inode){	struct logfs_inode *li = logfs_inode(inode);	int i; 	inode->i_mode	= be16_to_cpu(di->di_mode);	li->li_height	= di->di_height;	li->li_flags	= be32_to_cpu(di->di_flags);	i_uid_write(inode, be32_to_cpu(di->di_uid));	i_gid_write(inode, be32_to_cpu(di->di_gid));	inode->i_size	= be64_to_cpu(di->di_size);	logfs_set_blocks(inode, be64_to_cpu(di->di_used_bytes));	inode->i_atime	= be64_to_timespec(di->di_atime);	inode->i_ctime	= be64_to_timespec(di->di_ctime);	inode->i_mtime	= be64_to_timespec(di->di_mtime);	set_nlink(inode, be32_to_cpu(di->di_refcount));	inode->i_generation = be32_to_cpu(di->di_generation); 	switch (inode->i_mode & S_IFMT) {	case S_IFSOCK:	/* fall through */	case S_IFBLK:	/* fall through */	case S_IFCHR:	/* fall through */	case S_IFIFO:		inode->i_rdev = be64_to_cpu(di->di_data[0]);		break;	case S_IFDIR:	/* fall through */	case S_IFREG:	/* fall through */	case S_IFLNK:		for (i = 0; i < LOGFS_EMBEDDED_FIELDS; i++)			li->li_data[i] = be64_to_cpu(di->di_data[i]);		break;	default:		BUG();	}} static void logfs_inode_to_disk(struct inode *inode, struct logfs_disk_inode*di){	struct logfs_inode *li = logfs_inode(inode);	int i; 	di->di_mode	= cpu_to_be16(inode->i_mode);	di->di_height	= li->li_height;	di->di_pad	= 0;	di->di_flags	= cpu_to_be32(li->li_flags);	di->di_uid	= cpu_to_be32(i_uid_read(inode));	di->di_gid	= cpu_to_be32(i_gid_read(inode));	di->di_size	= cpu_to_be64(i_size_read(inode));	di->di_used_bytes = cpu_to_be64(li->li_used_bytes);	di->di_atime	= timespec_to_be64(inode->i_atime);	di->di_ctime	= timespec_to_be64(inode->i_ctime);	di->di_mtime	= timespec_to_be64(inode->i_mtime);	di->di_refcount	= cpu_to_be32(inode->i_nlink);	di->di_generation = cpu_to_be32(inode->i_generation); 	switch (inode->i_mode & S_IFMT) {	case S_IFSOCK:	/* fall through */	case S_IFBLK:	/* fall through */	case S_IFCHR:	/* fall through */	case S_IFIFO:		di->di_data[0] = cpu_to_be64(inode->i_rdev);		break;	case S_IFDIR:	/* fall through */	case S_IFREG:	/* fall through */	case S_IFLNK:		for (i = 0; i < LOGFS_EMBEDDED_FIELDS; i++)			di->di_data[i] = cpu_to_be64(li->li_data[i]);		break;	default:		BUG();	}} static void __logfs_set_blocks(struct inode *inode){	struct super_block *sb = inode->i_sb;	struct logfs_inode *li = logfs_inode(inode); 	inode->i_blocks = ULONG_MAX;	if (li->li_used_bytes >> sb->s_blocksize_bits < ULONG_MAX)		inode->i_blocks = ALIGN(li->li_used_bytes, 512) >> 9;} void logfs_set_blocks(struct inode *inode, u64 bytes){	struct logfs_inode *li = logfs_inode(inode); 	li->li_used_bytes = bytes;	__logfs_set_blocks(inode);} static void prelock_page(struct super_block *sb, struct page *page, int lock){	struct logfs_super *super = logfs_super(sb); 	BUG_ON(!PageLocked(page));	if (lock) {		BUG_ON(PagePreLocked(page));		SetPagePreLocked(page);	} else {		/* We are in GC path. */		if (PagePreLocked(page))			super->s_lock_count++;		else			SetPagePreLocked(page);	}} static void preunlock_page(struct super_block *sb, struct page *page, int lock){	struct logfs_super *super = logfs_super(sb); 	BUG_ON(!PageLocked(page));	if (lock)		ClearPagePreLocked(page);	else {		/* We are in GC path. */		BUG_ON(!PagePreLocked(page));		if (super->s_lock_count)			super->s_lock_count--;		else			ClearPagePreLocked(page);	}} /* * Logfs is prone to an AB-BA deadlock where one task tries to acquire * s_write_mutex with a locked page and GC tries to get that page while holding * s_write_mutex. * To solve this issue logfs will ignore the page lock iff the page in question * is waiting for s_write_mutex.  We annotate this fact by setting PG_pre_locked * in addition to PG_locked. */void logfs_get_wblocks(struct super_block *sb, struct page *page, int lock){	struct logfs_super *super = logfs_super(sb); 	if (page)		prelock_page(sb, page, lock); 	if (lock) {		mutex_lock(&super->s_write_mutex);		logfs_gc_pass(sb);		/* FIXME: We also have to check for shadowed space		 * and mempool fill grade */	}} void logfs_put_wblocks(struct super_block *sb, struct page *page, int lock){	struct logfs_super *super = logfs_super(sb); 	if (page)		preunlock_page(sb, page, lock);	/* Order matters - we must clear PG_pre_locked before releasing	 * s_write_mutex or we could race against another task. */	if (lock)		mutex_unlock(&super->s_write_mutex);} static struct page *logfs_get_read_page(struct inode *inode, u64 bix,		level_t level){	return find_or_create_page(inode->i_mapping,			logfs_pack_index(bix, level), GFP_NOFS);} static void logfs_put_read_page(struct page *page){	unlock_page(page);	page_cache_release(page);} static void logfs_lock_write_page(struct page *page){	int loop = 0; 	while (unlikely(!trylock_page(page))) {		if (loop++ > 0x1000) {			/* Has been observed once so far... */			printk(KERN_ERR "stack at %p\n", &loop);			BUG();		}		if (PagePreLocked(page)) {			/* Holder of page lock is waiting for us, it			 * is safe to use this page. */			break;		}		/* Some other process has this page locked and has		 * nothing to do with us.  Wait for it to finish.		 */		schedule();	}	BUG_ON(!PageLocked(page));} static struct page *logfs_get_write_page(struct inode *inode, u64 bix,		level_t level){	struct address_space *mapping = inode->i_mapping;	pgoff_t index = logfs_pack_index(bix, level);	struct page *page;	int err; repeat:	page = find_get_page(mapping, index);	if (!page) {		page = __page_cache_alloc(GFP_NOFS);		if (!page)			return NULL;		err = add_to_page_cache_lru(page, mapping, index, GFP_NOFS);		if (unlikely(err)) {			page_cache_release(page);			if (err == -EEXIST)				goto repeat;			return NULL;		}	} else logfs_lock_write_page(page);	BUG_ON(!PageLocked(page));	return page;} static void logfs_unlock_write_page(struct page *page){	if (!PagePreLocked(page))		unlock_page(page);} static void logfs_put_write_page(struct page *page){	logfs_unlock_write_page(page);	page_cache_release(page);} static struct page *logfs_get_page(struct inode *inode, u64 bix, level_t level,		int rw){	if (rw == READ)		return logfs_get_read_page(inode, bix, level);	else		return logfs_get_write_page(inode, bix, level);} static void logfs_put_page(struct page *page, int rw){	if (rw == READ)		logfs_put_read_page(page);	else		logfs_put_write_page(page);} static unsigned long __get_bits(u64 val, int skip, int no){	u64 ret = val; 	ret >>= skip * no;	ret <<= 64 - no;	ret >>= 64 - no;	return ret;} static unsigned long get_bits(u64 val, level_t skip){	return __get_bits(val, (__force int)skip, LOGFS_BLOCK_BITS);} static inline void init_shadow_tree(struct super_block *sb,		struct shadow_tree *tree){	struct logfs_super *super = logfs_super(sb); 	btree_init_mempool64(&tree->new, super->s_btree_pool);	btree_init_mempool64(&tree->old, super->s_btree_pool);} static void indirect_write_block(struct logfs_block *block){	struct page *page;	struct inode *inode;	int ret; 	page = block->page;	inode = page->mapping->host;	logfs_lock_write_page(page);	ret = logfs_write_buf(inode, page, 0);	logfs_unlock_write_page(page);	/*	 * This needs some rework.  Unless you want your filesystem to run	 * completely synchronously (you don't), the filesystem will always	 * report writes as 'successful' before the actual work has been	 * done.  The actual work gets done here and this is where any errors	 * will show up.  And there isn't much we can do about it, really.	 *	 * Some attempts to fix the errors (move from bad blocks, retry io,...)	 * have already been done, so anything left should be either a broken	 * device or a bug somewhere in logfs itself.  Being relatively new,	 * the odds currently favor a bug, so for now the line below isn't	 * entirely tasteles.	 */	BUG_ON(ret);} static void inode_write_block(struct logfs_block *block){	struct inode *inode;	int ret; 	inode = block->inode;	if (inode->i_ino == LOGFS_INO_MASTER)		logfs_write_anchor(inode->i_sb);	else {		ret = __logfs_write_inode(inode, NULL, 0);		/* see indirect_write_block comment */		BUG_ON(ret);	}} /* * This silences a false, yet annoying gcc warning.  I hate it when my editor * jumps into bitops.h each time I recompile this file. * TODO: Complain to gcc folks about this and upgrade compiler. */static unsigned long fnb(const unsigned long *addr,		unsigned long size, unsigned long offset){	return find_next_bit(addr, size, offset);} static __be64 inode_val0(struct inode *inode){	struct logfs_inode *li = logfs_inode(inode);	u64 val; 	/*	 * Explicit shifting generates good code, but must match the format	 * of the structure.  Add some paranoia just in case.	 */	BUILD_BUG_ON(offsetof(struct logfs_disk_inode, di_mode) != 0);	BUILD_BUG_ON(offsetof(struct logfs_disk_inode, di_height) != 2);	BUILD_BUG_ON(offsetof(struct logfs_disk_inode, di_flags) != 4); 	val =	(u64)inode->i_mode << 48 |		(u64)li->li_height << 40 |		(u64)li->li_flags;	return cpu_to_be64(val);} static int inode_write_alias(struct super_block *sb,		struct logfs_block *block, write_alias_t *write_one_alias){	struct inode *inode = block->inode;	struct logfs_inode *li = logfs_inode(inode);	unsigned long pos;	u64 ino , bix;	__be64 val;	level_t level;	int err; 	for (pos = 0; ; pos++) {		pos = fnb(block->alias_map, LOGFS_BLOCK_FACTOR, pos);		if (pos >= LOGFS_EMBEDDED_FIELDS + INODE_POINTER_OFS)			return 0; 		switch (pos) {		case INODE_HEIGHT_OFS:			val = inode_val0(inode);			break;		case INODE_USED_OFS:			val = cpu_to_be64(li->li_used_bytes);			break;		case INODE_SIZE_OFS:			val = cpu_to_be64(i_size_read(inode));			break;		case INODE_POINTER_OFS ... INODE_POINTER_OFS + LOGFS_EMBEDDED_FIELDS - 1:			val = cpu_to_be64(li->li_data[pos - INODE_POINTER_OFS]);			break;		default:			BUG();		} 		ino = LOGFS_INO_MASTER;		bix = inode->i_ino;		level = LEVEL(0);		err = write_one_alias(sb, ino, bix, level, pos, val);		if (err)			return err;	}

Lines 1–500 of 2299.