Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1760577AbZFMN1R (ORCPT ); Sat, 13 Jun 2009 09:27:17 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1763319AbZFMNZ6 (ORCPT ); Sat, 13 Jun 2009 09:25:58 -0400 Received: from mail-fx0-f216.google.com ([209.85.220.216]:37936 "EHLO mail-fx0-f216.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1763633AbZFMNZx (ORCPT ); Sat, 13 Jun 2009 09:25:53 -0400 DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=message-id:date:from:user-agent:mime-version:to:cc:subject :content-type:content-transfer-encoding; b=ZLZNKWL3OfqVUpbv8x6rmzJ6FpvWp2lnfbzjgBFsxH9tzTOMd5U/eJko+3E7OQLNib mBIka8ELrMAs5D9ezyw7OSUpFx68mcmu9FAhbFcYoqaBsugUpa/Jd5y5CzwDVutGO3QY WHGm88rDgAYNdS8XohyHidtykNOfw+GuUUV3U= Message-ID: <4A33A7F7.2000802@gmail.com> Date: Sat, 13 Jun 2009 15:21:59 +0200 From: Marco User-Agent: Thunderbird 2.0.0.19 (X11/20081227) MIME-Version: 1.0 To: Linux FS Devel CC: Linux Embedded , Linux Kernel , Daniel Walker Subject: [PATCH 07/14] Pramfs: Inode operations Content-Type: text/plain; charset=ISO-8859-15 Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 15977 Lines: 637 From: Marco Stornelli Inode operations. Signed-off-by: Marco Stornelli --- diff -uprN linux-2.6.30-orig/fs/pramfs/inode.c linux-2.6.30/fs/pramfs/inode.c --- linux-2.6.30-orig/fs/pramfs/inode.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.30/fs/pramfs/inode.c 2009-06-13 12:58:27.000000000 +0200 @@ -0,0 +1,620 @@ +/* + * FILE NAME fs/pramfs/inode.c + * + * BRIEF DESCRIPTION + * + * Inode methods (allocate/free/read/write). + * + * Copyright 2009 Marco Stornelli + * Copyright 2003 Sony Corporation + * Copyright 2003 Matsushita Electric Industrial Co., Ltd. + * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "pram_fs.h" +#include "xip.h" + +static struct backing_dev_info pram_backing_dev_info = { + .ra_pages = 0, /* No readahead */ + .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK, +}; + +/* + * allocate a data block for inode and return it's absolute blocknr. + * Zeroes out the block if zero set. Increments inode->i_blocks. + */ +static int +pram_new_data_block(struct inode *inode, int *blocknr, int zero) +{ + unsigned long flags; + + int errval = pram_new_block(inode->i_sb, blocknr, zero); + + flags = 0; + + if (!errval) { + struct pram_inode *pi = pram_get_inode(inode->i_sb, + inode->i_ino); + inode->i_blocks++; + pram_lock_inode(pi, flags); + pi->i_blocks = inode->i_blocks; + pram_unlock_inode(pi, flags); + } + + return errval; +} + +/* + * find the offset to the block represented by the given inode's file + * relative block number. + */ +off_t pram_find_data_block(struct inode *inode, int file_blocknr) +{ + struct super_block *sb = inode->i_sb; + struct pram_inode *pi; + off_t *row; /* ptr to row block */ + off_t *col; /* ptr to column blocks */ + off_t bp = 0; + int i_row, i_col; + int N = sb->s_blocksize >> 2; /* num block ptrs per block */ + int Nbits = sb->s_blocksize_bits - 2; + + pi = pram_get_inode(sb, inode->i_ino); + + i_row = file_blocknr >> Nbits; + i_col = file_blocknr & (N-1); + + row = pram_get_block(sb, pi->i_type.reg.row_block); + if (row) { + col = pram_get_block(sb, row[i_row]); + if (col) + bp = col[i_col]; + } + + return bp; +} + + +/* + * Free data blocks from inode starting at first_trunc_block. + */ +static void +pram_truncate_blocks(struct inode *inode, int first_trunc_block) +{ + struct super_block *sb = inode->i_sb; + struct pram_inode *pi = pram_get_inode(sb, inode->i_ino); + int N = sb->s_blocksize >> 2; /* num block ptrs per block */ + int Nbits = sb->s_blocksize_bits - 2; + int first_row_index, last_row_index; + int i, j, first_blocknr, last_blocknr, blocknr; + unsigned long flags; + off_t *row; /* ptr to row block */ + off_t *col; /* ptr to column blocks */ + + flags = 0; + + if (first_trunc_block >= inode->i_blocks || + !inode->i_blocks || !pi->i_type.reg.row_block) { + return; + } + + first_blocknr = first_trunc_block; + last_blocknr = inode->i_blocks - 1; + first_row_index = first_blocknr >> Nbits; + last_row_index = last_blocknr >> Nbits; + + row = pram_get_block(sb, pi->i_type.reg.row_block); + + for (i = first_row_index; i <= last_row_index; i++) { + int first_col_index = (i == first_row_index) ? + first_blocknr & (N-1) : 0; + int last_col_index = (i == last_row_index) ? + last_blocknr & (N-1) : N-1; + + col = pram_get_block(sb, row[i]); + for (j = first_col_index; j <= last_col_index; j++) { + blocknr = pram_get_blocknr(sb, col[j]); + pram_free_block(sb, blocknr); + pram_lock_block(sb, col, flags); + col[j] = 0; + pram_unlock_block(sb, col, flags); + } + + if (first_col_index == 0) { + blocknr = pram_get_blocknr(sb, row[i]); + pram_free_block(sb, blocknr); + pram_lock_block(sb, row, flags); + row[i] = 0; + pram_unlock_block(sb, row, flags); + } + } + + inode->i_blocks -= (last_blocknr - first_blocknr + 1); + + if (first_row_index == 0) { + blocknr = pram_get_blocknr(sb, pi->i_type.reg.row_block); + pram_free_block(sb, blocknr); + pram_lock_inode(pi, flags); + pi->i_type.reg.row_block = 0; + pram_unlock_inode(pi, flags); + } + pram_lock_inode(pi, flags); + pi->i_blocks = inode->i_blocks; + pram_unlock_inode(pi, flags); +} + +/* + * Allocate num data blocks for inode, starting at given file-relative + * block number. Any unallocated file blocks before file_blocknr + * are allocated. All blocks except the last are zeroed out. + */ +int pram_alloc_blocks(struct inode *inode, int file_blocknr, int num) +{ + struct super_block *sb = inode->i_sb; + struct pram_inode *pi = pram_get_inode(sb, inode->i_ino); + int N = sb->s_blocksize >> 2; /* num block ptrs per block */ + int Nbits = sb->s_blocksize_bits - 2; + int first_file_blocknr; + int last_file_blocknr; + int first_row_index, last_row_index; + int i, j, blocknr, errval; + unsigned long flags; + off_t *row; + off_t *col; + + flags = 0; + + if (!pi->i_type.reg.row_block) { + /* alloc the 2nd order array block */ + errval = pram_new_block(sb, &blocknr, 1); + if (errval) { + pram_err("failed to alloc 2nd order array block\n"); + goto fail; + } + pram_lock_inode(pi, flags); + pi->i_type.reg.row_block = pram_get_block_off(sb, blocknr); + pram_unlock_inode(pi, flags); + } + + row = pram_get_block(sb, pi->i_type.reg.row_block); + + first_file_blocknr = (file_blocknr > inode->i_blocks) ? + inode->i_blocks : file_blocknr; + last_file_blocknr = file_blocknr + num - 1; + + first_row_index = first_file_blocknr >> Nbits; + last_row_index = last_file_blocknr >> Nbits; + + for (i = first_row_index; i <= last_row_index; i++) { + int first_col_index, last_col_index; + /* + * we are starting a new row, so make sure + * there is a block allocated for the row. + */ + if (!row[i]) { + /* allocate the row block */ + errval = pram_new_block(sb, &blocknr, 1); + if (errval) { + pram_err("failed to alloc row block\n"); + goto fail; + } + pram_lock_block(sb, row, flags); + row[i] = pram_get_block_off(sb, blocknr); + pram_unlock_block(sb, row, flags); + } + col = pram_get_block(sb, row[i]); + + first_col_index = (i == first_row_index) ? + first_file_blocknr & (N-1) : 0; + + last_col_index = (i == last_row_index) ? + last_file_blocknr & (N-1) : N-1; + + for (j = first_col_index; j <= last_col_index; j++) { + int last_block = + (i == last_row_index) && (j == last_col_index); + if (!col[j]) { + errval = pram_new_data_block(inode, + &blocknr, + !last_block); + if (errval) { + pram_err("failed to alloc " + "data block\n"); + goto fail; + } + pram_lock_block(sb, col, flags); + col[j] = pram_get_block_off(sb, blocknr); + pram_unlock_block(sb, col, flags); + } + } + } + + errval = 0; + fail: + return errval; +} + +static int +pram_fill_inode(struct inode *inode, struct pram_inode *pi) +{ + int ret = -EIO; + + if (pram_calc_checksum((u32 *)pi, PRAM_INODE_SIZE>>2)) { + pram_err("checksum error in inode %08x\n", + (u32)inode->i_ino); + goto bad_inode; + } + + inode->i_mode = pi->i_mode; + inode->i_uid = pi->i_uid; + inode->i_gid = pi->i_gid; + inode->i_nlink = pi->i_links_count; + inode->i_size = pi->i_size; + inode->i_atime.tv_sec = pi->i_atime; + inode->i_ctime.tv_sec = pi->i_ctime; + inode->i_mtime.tv_sec = pi->i_mtime; + inode->i_atime.tv_nsec = inode->i_mtime.tv_nsec = + inode->i_ctime.tv_nsec = 0; + + /* check if the inode is active. */ + if (inode->i_nlink == 0 && (inode->i_mode == 0 || pi->i_dtime)) { + /* this inode is deleted */ + ret = -EINVAL; + goto bad_inode; + } + + inode->i_blocks = pi->i_blocks; + inode->i_ino = pram_get_inodenr(inode->i_sb, pi); + inode->i_mapping->a_ops = &pram_aops; + inode->i_mapping->backing_dev_info = &pram_backing_dev_info; + + insert_inode_hash(inode); + switch (inode->i_mode & S_IFMT) { + case S_IFREG: + inode->i_op = &pram_file_inode_operations; + inode->i_fop = &pram_file_operations; + break; + case S_IFDIR: + inode->i_op = &pram_dir_inode_operations; + inode->i_fop = &pram_dir_operations; + break; + case S_IFLNK: + inode->i_op = &pram_symlink_inode_operations; + break; + default: + inode->i_size = 0; + init_special_inode(inode, inode->i_mode, + pi->i_type.dev.rdev); + break; + } + + return 0; + + bad_inode: + make_bad_inode(inode); + return ret; +} + +static int pram_update_inode(struct inode *inode) +{ + struct pram_inode *pi; + int retval = 0; + unsigned long flags; + + flags = 0; + + pi = pram_get_inode(inode->i_sb, inode->i_ino); + + pram_lock_inode(pi, flags); + pi->i_mode = inode->i_mode; + pi->i_uid = inode->i_uid; + pi->i_gid = inode->i_gid; + pi->i_links_count = inode->i_nlink; + pi->i_size = inode->i_size; + pi->i_blocks = inode->i_blocks; + pi->i_atime = inode->i_atime.tv_sec; + pi->i_ctime = inode->i_ctime.tv_sec; + pi->i_mtime = inode->i_mtime.tv_sec; + + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + pi->i_type.dev.rdev = inode->i_rdev; + + pram_unlock_inode(pi, flags); + return retval; +} + +/* + * NOTE! When we get the inode, we're the only people + * that have access to it, and as such there are no + * race conditions we have to worry about. The inode + * is not on the hash-lists, and it cannot be reached + * through the filesystem because the directory entry + * has been deleted earlier. + */ +static void pram_free_inode(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct pram_super_block *ps; + struct pram_inode *pi; + unsigned long inode_nr; + unsigned long flags; + + flags = 0; + + /* + * Note: we must free any quota before locking the superblock, + * as writing the quota to disk may need the lock as well. + */ + if (!is_bad_inode(inode)) { + /* Quota is already initialized in iput() */ + vfs_dq_free_inode(inode); + vfs_dq_drop(inode); + } + + lock_super(sb); + clear_inode(inode); + + inode_nr = (inode->i_ino - PRAM_ROOT_INO) >> PRAM_INODE_BITS; + + pi = pram_get_inode(sb, inode->i_ino); + pram_lock_inode(pi, flags); + pi->i_dtime = get_seconds(); + pi->i_type.reg.row_block = 0; + pram_unlock_inode(pi, flags); + + /* increment s_free_inodes_count */ + ps = pram_get_super(sb); + pram_lock_super(ps, flags); + if (inode_nr < ps->s_free_inode_hint) + ps->s_free_inode_hint = inode_nr; + ps->s_free_inodes_count++; + if (ps->s_free_inodes_count == ps->s_inodes_count - 1) { + /* filesystem is empty */ + pram_dbg("fs is empty!\n"); + ps->s_free_inode_hint = 1; + } + pram_unlock_super(ps, flags); + + unlock_super(sb); +} + + +struct inode * +pram_fill_new_inode(struct super_block *sb, + struct pram_inode *pi) +{ + struct inode *inode = new_inode(sb); + + if (inode) + pram_fill_inode(inode, pi); + + return inode; +} + + +/* + * Called at the last iput() if i_nlink is zero. + */ +void pram_delete_inode(struct inode *inode) +{ + lock_kernel(); + + if (is_bad_inode(inode)) + goto no_delete; + + /* unlink from chain in the inode's directory */ + pram_remove_link(inode); + inode->i_size = 0; + if (inode->i_blocks) + pram_truncate(inode); + pram_free_inode(inode); + + unlock_kernel(); + return; + no_delete: + unlock_kernel(); + clear_inode(inode); /* We must guarantee clearing of inode... */ +} + + +struct inode *pram_new_inode(const struct inode *dir, int mode) +{ + struct super_block *sb; + struct pram_super_block *ps; + struct inode *inode; + struct pram_inode *pi = NULL; + int i, errval; + ino_t ino = 0; + unsigned long flags; + + flags = 0; + + sb = dir->i_sb; + inode = new_inode(sb); + if (!inode) + return ERR_PTR(-ENOMEM); + + lock_super(sb); + ps = pram_get_super(sb); + + if (ps->s_free_inodes_count) { + /* find the oldest unused pram inode */ + for (i = ps->s_free_inode_hint; i < ps->s_inodes_count; i++) { + ino = PRAM_ROOT_INO + (i << PRAM_INODE_BITS); + pi = pram_get_inode(sb, ino); + /* check if the inode is active. */ + if (pi->i_links_count == 0 && (pi->i_mode == 0 || + pi->i_dtime)) { + /* this inode is deleted */ + break; + } + } + + if (i >= ps->s_inodes_count) { + pram_err("s_free_inodes_count!=0 but none free!?\n"); + errval = -ENOSPC; + goto fail; + } + + pram_dbg("allocating inode %lu\n", ino); + pram_lock_super(ps, flags); + ps->s_free_inodes_count--; + ps->s_free_inode_hint = (i < ps->s_inodes_count-1) ? i+1 : 0; + pram_unlock_super(ps, flags); + } else { + pram_err("no space left to create new inode!\n"); + errval = -ENOSPC; + goto fail; + } + + /* chosen inode is in ino */ + + inode->i_ino = ino; + inode->i_uid = current_fsuid(); + + if (dir->i_mode & S_ISGID) { + inode->i_gid = dir->i_gid; + if (S_ISDIR(mode)) + mode |= S_ISGID; + } else + inode->i_gid = current_fsgid(); + inode->i_mode = mode; + + inode->i_blocks = inode->i_size = 0; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + + pram_lock_inode(pi, flags); + pi->i_d.d_next = 0; + pi->i_d.d_prev = 0; + pram_unlock_inode(pi, flags); + + insert_inode_hash(inode); + pram_write_inode(inode, 0); + + unlock_super(sb); + + if (vfs_dq_alloc_inode(inode)) { + vfs_dq_drop(inode); + inode->i_flags |= S_NOQUOTA; + inode->i_nlink = 0; + iput(inode); + return ERR_PTR(-EDQUOT); + } + return inode; + +fail: + unlock_super(sb); + make_bad_inode(inode); + iput(inode); + return ERR_PTR(errval); +} + +void pram_truncate(struct inode *inode) +{ + int blocksize, blocksize_bits; + int blocknr; + + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) + return; + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + return; + + blocksize_bits = inode->i_sb->s_blocksize_bits; + blocksize = 1 << blocksize_bits; + blocknr = (inode->i_size + blocksize-1) >> blocksize_bits; + + lock_kernel(); + pram_truncate_blocks(inode, blocknr); + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + pram_update_inode(inode); + unlock_kernel(); +} + +int pram_write_inode(struct inode *inode, int wait) +{ + int ret = 0; + + lock_kernel(); + ret = pram_update_inode(inode); + unlock_kernel(); + + return ret; +} + +/* + * dirty_inode() is called from __mark_inode_dirty() + */ +void pram_dirty_inode(struct inode *inode) +{ + pram_write_inode(inode, 0); +} + +int pram_get_and_update_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh, int create) +{ + struct super_block *sb = inode->i_sb; + unsigned int blocksize = 1 << inode->i_blkbits; + int err = -EIO; + off_t block; + void *bp; + unsigned long flags; + + flags = 0; + + lock_kernel(); + + block = pram_find_data_block(inode, iblock); + + if (!block) { + if (!create) + goto out; + + err = pram_alloc_blocks(inode, iblock, 1); + if (err) + goto out; + block = pram_find_data_block(inode, iblock); + if (!block) { + err = -EIO; + goto out; + } + set_buffer_new(bh); + } + + bh->b_blocknr = block; + set_buffer_mapped(bh); + + /* now update the buffer synchronously */ + bp = pram_get_block(sb, block); + if (buffer_new(bh)) { + pram_lock_block(sb, bp, flags); + memset(bp, 0, blocksize); + pram_unlock_block(sb, bp, flags); + memset(bh->b_data, 0, blocksize); + } else { + memcpy(bh->b_data, bp, blocksize); + } + + set_buffer_uptodate(bh); + err = 0; + + out: + unlock_kernel(); + return err; +} + +struct address_space_operations pram_aops = { + .readpage = pram_readpage, + .direct_IO = pram_direct_IO, + .get_xip_mem = pram_get_xip_mem, +}; -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/