mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-09 01:28:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
50
fs/jfs/Kconfig
Normal file
50
fs/jfs/Kconfig
Normal file
|
@ -0,0 +1,50 @@
|
|||
config JFS_FS
|
||||
tristate "JFS filesystem support"
|
||||
select NLS
|
||||
select CRC32
|
||||
help
|
||||
This is a port of IBM's Journaled Filesystem . More information is
|
||||
available in the file <file:Documentation/filesystems/jfs.txt>.
|
||||
|
||||
If you do not intend to use the JFS filesystem, say N.
|
||||
|
||||
config JFS_POSIX_ACL
|
||||
bool "JFS POSIX Access Control Lists"
|
||||
depends on JFS_FS
|
||||
select FS_POSIX_ACL
|
||||
help
|
||||
Posix Access Control Lists (ACLs) support permissions for users and
|
||||
groups beyond the owner/group/world scheme.
|
||||
|
||||
To learn more about Access Control Lists, visit the Posix ACLs for
|
||||
Linux website <http://acl.bestbits.at/>.
|
||||
|
||||
If you don't know what Access Control Lists are, say N
|
||||
|
||||
config JFS_SECURITY
|
||||
bool "JFS Security Labels"
|
||||
depends on JFS_FS
|
||||
help
|
||||
Security labels support alternative access control models
|
||||
implemented by security modules like SELinux. This option
|
||||
enables an extended attribute handler for file security
|
||||
labels in the jfs filesystem.
|
||||
|
||||
If you are not using a security module that requires using
|
||||
extended attributes for file security labels, say N.
|
||||
|
||||
config JFS_DEBUG
|
||||
bool "JFS debugging"
|
||||
depends on JFS_FS
|
||||
help
|
||||
If you are experiencing any problems with the JFS filesystem, say
|
||||
Y here. This will result in additional debugging messages to be
|
||||
written to the system log. Under normal circumstances, this
|
||||
results in very little overhead.
|
||||
|
||||
config JFS_STATISTICS
|
||||
bool "JFS statistics"
|
||||
depends on JFS_FS
|
||||
help
|
||||
Enabling this option will cause statistics from the JFS file system
|
||||
to be made available to the user in the /proc/fs/jfs/ directory.
|
16
fs/jfs/Makefile
Normal file
16
fs/jfs/Makefile
Normal file
|
@ -0,0 +1,16 @@
|
|||
#
|
||||
# Makefile for the Linux JFS filesystem routines.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_JFS_FS) += jfs.o
|
||||
|
||||
jfs-y := super.o file.o inode.o namei.o jfs_mount.o jfs_umount.o \
|
||||
jfs_xtree.o jfs_imap.o jfs_debug.o jfs_dmap.o \
|
||||
jfs_unicode.o jfs_dtree.o jfs_inode.o jfs_discard.o \
|
||||
jfs_extent.o symlink.o jfs_metapage.o \
|
||||
jfs_logmgr.o jfs_txnmgr.o jfs_uniupr.o \
|
||||
resize.o xattr.o ioctl.o
|
||||
|
||||
jfs-$(CONFIG_JFS_POSIX_ACL) += acl.o
|
||||
|
||||
ccflags-y := -D_JFS_4K
|
161
fs/jfs/acl.c
Normal file
161
fs/jfs/acl.c
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2002-2004
|
||||
* Copyright (C) Andreas Gruenbacher, 2001
|
||||
* Copyright (C) Linus Torvalds, 1991, 1992
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/posix_acl_xattr.h>
|
||||
#include "jfs_incore.h"
|
||||
#include "jfs_txnmgr.h"
|
||||
#include "jfs_xattr.h"
|
||||
#include "jfs_acl.h"
|
||||
|
||||
struct posix_acl *jfs_get_acl(struct inode *inode, int type)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
char *ea_name;
|
||||
int size;
|
||||
char *value = NULL;
|
||||
|
||||
acl = get_cached_acl(inode, type);
|
||||
if (acl != ACL_NOT_CACHED)
|
||||
return acl;
|
||||
|
||||
switch(type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
ea_name = POSIX_ACL_XATTR_ACCESS;
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
ea_name = POSIX_ACL_XATTR_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
size = __jfs_getxattr(inode, ea_name, NULL, 0);
|
||||
|
||||
if (size > 0) {
|
||||
value = kmalloc(size, GFP_KERNEL);
|
||||
if (!value)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
size = __jfs_getxattr(inode, ea_name, value, size);
|
||||
}
|
||||
|
||||
if (size < 0) {
|
||||
if (size == -ENODATA)
|
||||
acl = NULL;
|
||||
else
|
||||
acl = ERR_PTR(size);
|
||||
} else {
|
||||
acl = posix_acl_from_xattr(&init_user_ns, value, size);
|
||||
}
|
||||
kfree(value);
|
||||
if (!IS_ERR(acl))
|
||||
set_cached_acl(inode, type, acl);
|
||||
return acl;
|
||||
}
|
||||
|
||||
static int __jfs_set_acl(tid_t tid, struct inode *inode, int type,
|
||||
struct posix_acl *acl)
|
||||
{
|
||||
char *ea_name;
|
||||
int rc;
|
||||
int size = 0;
|
||||
char *value = NULL;
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
ea_name = POSIX_ACL_XATTR_ACCESS;
|
||||
if (acl) {
|
||||
rc = posix_acl_equiv_mode(acl, &inode->i_mode);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
inode->i_ctime = CURRENT_TIME;
|
||||
mark_inode_dirty(inode);
|
||||
if (rc == 0)
|
||||
acl = NULL;
|
||||
}
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
ea_name = POSIX_ACL_XATTR_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (acl) {
|
||||
size = posix_acl_xattr_size(acl->a_count);
|
||||
value = kmalloc(size, GFP_KERNEL);
|
||||
if (!value)
|
||||
return -ENOMEM;
|
||||
rc = posix_acl_to_xattr(&init_user_ns, acl, value, size);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
}
|
||||
rc = __jfs_setxattr(tid, inode, ea_name, value, size, 0);
|
||||
out:
|
||||
kfree(value);
|
||||
|
||||
if (!rc)
|
||||
set_cached_acl(inode, type, acl);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int jfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
||||
{
|
||||
int rc;
|
||||
tid_t tid;
|
||||
|
||||
tid = txBegin(inode->i_sb, 0);
|
||||
mutex_lock(&JFS_IP(inode)->commit_mutex);
|
||||
rc = __jfs_set_acl(tid, inode, type, acl);
|
||||
if (!rc)
|
||||
rc = txCommit(tid, 1, &inode, 0);
|
||||
txEnd(tid);
|
||||
mutex_unlock(&JFS_IP(inode)->commit_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int jfs_init_acl(tid_t tid, struct inode *inode, struct inode *dir)
|
||||
{
|
||||
struct posix_acl *default_acl, *acl;
|
||||
int rc = 0;
|
||||
|
||||
rc = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (default_acl) {
|
||||
rc = __jfs_set_acl(tid, inode, ACL_TYPE_DEFAULT, default_acl);
|
||||
posix_acl_release(default_acl);
|
||||
}
|
||||
|
||||
if (acl) {
|
||||
if (!rc)
|
||||
rc = __jfs_set_acl(tid, inode, ACL_TYPE_ACCESS, acl);
|
||||
posix_acl_release(acl);
|
||||
}
|
||||
|
||||
JFS_IP(inode)->mode2 = (JFS_IP(inode)->mode2 & 0xffff0000) |
|
||||
inode->i_mode;
|
||||
|
||||
return rc;
|
||||
}
|
49
fs/jfs/endian24.h
Normal file
49
fs/jfs/endian24.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2001
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_ENDIAN24
|
||||
#define _H_ENDIAN24
|
||||
|
||||
/*
|
||||
* endian24.h:
|
||||
*
|
||||
* Endian conversion for 24-byte data
|
||||
*
|
||||
*/
|
||||
#define __swab24(x) \
|
||||
({ \
|
||||
__u32 __x = (x); \
|
||||
((__u32)( \
|
||||
((__x & (__u32)0x000000ffUL) << 16) | \
|
||||
(__x & (__u32)0x0000ff00UL) | \
|
||||
((__x & (__u32)0x00ff0000UL) >> 16) )); \
|
||||
})
|
||||
|
||||
#if (defined(__KERNEL__) && defined(__LITTLE_ENDIAN)) || (defined(__BYTE_ORDER) && (__BYTE_ORDER == __LITTLE_ENDIAN))
|
||||
#define __cpu_to_le24(x) ((__u32)(x))
|
||||
#define __le24_to_cpu(x) ((__u32)(x))
|
||||
#else
|
||||
#define __cpu_to_le24(x) __swab24(x)
|
||||
#define __le24_to_cpu(x) __swab24(x)
|
||||
#endif
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#define cpu_to_le24 __cpu_to_le24
|
||||
#define le24_to_cpu __le24_to_cpu
|
||||
#endif
|
||||
|
||||
#endif /* !_H_ENDIAN24 */
|
167
fs/jfs/file.c
Normal file
167
fs/jfs/file.c
Normal file
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2002
|
||||
* Portions Copyright (C) Christoph Hellwig, 2001-2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/posix_acl.h>
|
||||
#include <linux/quotaops.h>
|
||||
#include "jfs_incore.h"
|
||||
#include "jfs_inode.h"
|
||||
#include "jfs_dmap.h"
|
||||
#include "jfs_txnmgr.h"
|
||||
#include "jfs_xattr.h"
|
||||
#include "jfs_acl.h"
|
||||
#include "jfs_debug.h"
|
||||
|
||||
int jfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
{
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
int rc = 0;
|
||||
|
||||
rc = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
if (!(inode->i_state & I_DIRTY) ||
|
||||
(datasync && !(inode->i_state & I_DIRTY_DATASYNC))) {
|
||||
/* Make sure committed changes hit the disk */
|
||||
jfs_flush_journal(JFS_SBI(inode->i_sb)->log, 1);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc |= jfs_commit_inode(inode, 1);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
return rc ? -EIO : 0;
|
||||
}
|
||||
|
||||
static int jfs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if ((rc = dquot_file_open(inode, file)))
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* We attempt to allow only one "active" file open per aggregate
|
||||
* group. Otherwise, appending to files in parallel can cause
|
||||
* fragmentation within the files.
|
||||
*
|
||||
* If the file is empty, it was probably just created and going
|
||||
* to be written to. If it has a size, we'll hold off until the
|
||||
* file is actually grown.
|
||||
*/
|
||||
if (S_ISREG(inode->i_mode) && file->f_mode & FMODE_WRITE &&
|
||||
(inode->i_size == 0)) {
|
||||
struct jfs_inode_info *ji = JFS_IP(inode);
|
||||
spin_lock_irq(&ji->ag_lock);
|
||||
if (ji->active_ag == -1) {
|
||||
struct jfs_sb_info *jfs_sb = JFS_SBI(inode->i_sb);
|
||||
ji->active_ag = BLKTOAG(addressPXD(&ji->ixpxd), jfs_sb);
|
||||
atomic_inc( &jfs_sb->bmap->db_active[ji->active_ag]);
|
||||
}
|
||||
spin_unlock_irq(&ji->ag_lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int jfs_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct jfs_inode_info *ji = JFS_IP(inode);
|
||||
|
||||
spin_lock_irq(&ji->ag_lock);
|
||||
if (ji->active_ag != -1) {
|
||||
struct bmap *bmap = JFS_SBI(inode->i_sb)->bmap;
|
||||
atomic_dec(&bmap->db_active[ji->active_ag]);
|
||||
ji->active_ag = -1;
|
||||
}
|
||||
spin_unlock_irq(&ji->ag_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jfs_setattr(struct dentry *dentry, struct iattr *iattr)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
int rc;
|
||||
|
||||
rc = inode_change_ok(inode, iattr);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (is_quota_modification(inode, iattr))
|
||||
dquot_initialize(inode);
|
||||
if ((iattr->ia_valid & ATTR_UID && !uid_eq(iattr->ia_uid, inode->i_uid)) ||
|
||||
(iattr->ia_valid & ATTR_GID && !gid_eq(iattr->ia_gid, inode->i_gid))) {
|
||||
rc = dquot_transfer(inode, iattr);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if ((iattr->ia_valid & ATTR_SIZE) &&
|
||||
iattr->ia_size != i_size_read(inode)) {
|
||||
inode_dio_wait(inode);
|
||||
|
||||
rc = inode_newsize_ok(inode, iattr->ia_size);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
truncate_setsize(inode, iattr->ia_size);
|
||||
jfs_truncate(inode);
|
||||
}
|
||||
|
||||
setattr_copy(inode, iattr);
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
if (iattr->ia_valid & ATTR_MODE)
|
||||
rc = posix_acl_chmod(inode, inode->i_mode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
const struct inode_operations jfs_file_inode_operations = {
|
||||
.setxattr = jfs_setxattr,
|
||||
.getxattr = jfs_getxattr,
|
||||
.listxattr = jfs_listxattr,
|
||||
.removexattr = jfs_removexattr,
|
||||
.setattr = jfs_setattr,
|
||||
#ifdef CONFIG_JFS_POSIX_ACL
|
||||
.get_acl = jfs_get_acl,
|
||||
.set_acl = jfs_set_acl,
|
||||
#endif
|
||||
};
|
||||
|
||||
const struct file_operations jfs_file_operations = {
|
||||
.open = jfs_open,
|
||||
.llseek = generic_file_llseek,
|
||||
.write = new_sync_write,
|
||||
.read = new_sync_read,
|
||||
.read_iter = generic_file_read_iter,
|
||||
.write_iter = generic_file_write_iter,
|
||||
.mmap = generic_file_mmap,
|
||||
.splice_read = generic_file_splice_read,
|
||||
.splice_write = iter_file_splice_write,
|
||||
.fsync = jfs_fsync,
|
||||
.release = jfs_release,
|
||||
.unlocked_ioctl = jfs_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = jfs_compat_ioctl,
|
||||
#endif
|
||||
};
|
423
fs/jfs/inode.c
Normal file
423
fs/jfs/inode.c
Normal file
|
@ -0,0 +1,423 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2004
|
||||
* Portions Copyright (C) Christoph Hellwig, 2001-2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mpage.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/quotaops.h>
|
||||
#include <linux/writeback.h>
|
||||
#include <linux/aio.h>
|
||||
#include "jfs_incore.h"
|
||||
#include "jfs_inode.h"
|
||||
#include "jfs_filsys.h"
|
||||
#include "jfs_imap.h"
|
||||
#include "jfs_extent.h"
|
||||
#include "jfs_unicode.h"
|
||||
#include "jfs_debug.h"
|
||||
|
||||
|
||||
struct inode *jfs_iget(struct super_block *sb, unsigned long ino)
|
||||
{
|
||||
struct inode *inode;
|
||||
int ret;
|
||||
|
||||
inode = iget_locked(sb, ino);
|
||||
if (!inode)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (!(inode->i_state & I_NEW))
|
||||
return inode;
|
||||
|
||||
ret = diRead(inode);
|
||||
if (ret < 0) {
|
||||
iget_failed(inode);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
if (S_ISREG(inode->i_mode)) {
|
||||
inode->i_op = &jfs_file_inode_operations;
|
||||
inode->i_fop = &jfs_file_operations;
|
||||
inode->i_mapping->a_ops = &jfs_aops;
|
||||
} else if (S_ISDIR(inode->i_mode)) {
|
||||
inode->i_op = &jfs_dir_inode_operations;
|
||||
inode->i_fop = &jfs_dir_operations;
|
||||
} else if (S_ISLNK(inode->i_mode)) {
|
||||
if (inode->i_size >= IDATASIZE) {
|
||||
inode->i_op = &page_symlink_inode_operations;
|
||||
inode->i_mapping->a_ops = &jfs_aops;
|
||||
} else {
|
||||
inode->i_op = &jfs_fast_symlink_inode_operations;
|
||||
/*
|
||||
* The inline data should be null-terminated, but
|
||||
* don't let on-disk corruption crash the kernel
|
||||
*/
|
||||
JFS_IP(inode)->i_inline[inode->i_size] = '\0';
|
||||
}
|
||||
} else {
|
||||
inode->i_op = &jfs_file_inode_operations;
|
||||
init_special_inode(inode, inode->i_mode, inode->i_rdev);
|
||||
}
|
||||
unlock_new_inode(inode);
|
||||
return inode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Workhorse of both fsync & write_inode
|
||||
*/
|
||||
int jfs_commit_inode(struct inode *inode, int wait)
|
||||
{
|
||||
int rc = 0;
|
||||
tid_t tid;
|
||||
static int noisy = 5;
|
||||
|
||||
jfs_info("In jfs_commit_inode, inode = 0x%p", inode);
|
||||
|
||||
/*
|
||||
* Don't commit if inode has been committed since last being
|
||||
* marked dirty, or if it has been deleted.
|
||||
*/
|
||||
if (inode->i_nlink == 0 || !test_cflag(COMMIT_Dirty, inode))
|
||||
return 0;
|
||||
|
||||
if (isReadOnly(inode)) {
|
||||
/* kernel allows writes to devices on read-only
|
||||
* partitions and may think inode is dirty
|
||||
*/
|
||||
if (!special_file(inode->i_mode) && noisy) {
|
||||
jfs_err("jfs_commit_inode(0x%p) called on "
|
||||
"read-only volume", inode);
|
||||
jfs_err("Is remount racy?");
|
||||
noisy--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
tid = txBegin(inode->i_sb, COMMIT_INODE);
|
||||
mutex_lock(&JFS_IP(inode)->commit_mutex);
|
||||
|
||||
/*
|
||||
* Retest inode state after taking commit_mutex
|
||||
*/
|
||||
if (inode->i_nlink && test_cflag(COMMIT_Dirty, inode))
|
||||
rc = txCommit(tid, 1, &inode, wait ? COMMIT_SYNC : 0);
|
||||
|
||||
txEnd(tid);
|
||||
mutex_unlock(&JFS_IP(inode)->commit_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int jfs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
{
|
||||
int wait = wbc->sync_mode == WB_SYNC_ALL;
|
||||
|
||||
if (inode->i_nlink == 0)
|
||||
return 0;
|
||||
/*
|
||||
* If COMMIT_DIRTY is not set, the inode isn't really dirty.
|
||||
* It has been committed since the last change, but was still
|
||||
* on the dirty inode list.
|
||||
*/
|
||||
if (!test_cflag(COMMIT_Dirty, inode)) {
|
||||
/* Make sure committed changes hit the disk */
|
||||
jfs_flush_journal(JFS_SBI(inode->i_sb)->log, wait);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (jfs_commit_inode(inode, wait)) {
|
||||
jfs_err("jfs_write_inode: jfs_commit_inode failed!");
|
||||
return -EIO;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void jfs_evict_inode(struct inode *inode)
|
||||
{
|
||||
jfs_info("In jfs_evict_inode, inode = 0x%p", inode);
|
||||
|
||||
if (!inode->i_nlink && !is_bad_inode(inode)) {
|
||||
dquot_initialize(inode);
|
||||
|
||||
if (JFS_IP(inode)->fileset == FILESYSTEM_I) {
|
||||
truncate_inode_pages_final(&inode->i_data);
|
||||
|
||||
if (test_cflag(COMMIT_Freewmap, inode))
|
||||
jfs_free_zero_link(inode);
|
||||
|
||||
diFree(inode);
|
||||
|
||||
/*
|
||||
* Free the inode from the quota allocation.
|
||||
*/
|
||||
dquot_initialize(inode);
|
||||
dquot_free_inode(inode);
|
||||
}
|
||||
} else {
|
||||
truncate_inode_pages_final(&inode->i_data);
|
||||
}
|
||||
clear_inode(inode);
|
||||
dquot_drop(inode);
|
||||
}
|
||||
|
||||
void jfs_dirty_inode(struct inode *inode, int flags)
|
||||
{
|
||||
static int noisy = 5;
|
||||
|
||||
if (isReadOnly(inode)) {
|
||||
if (!special_file(inode->i_mode) && noisy) {
|
||||
/* kernel allows writes to devices on read-only
|
||||
* partitions and may try to mark inode dirty
|
||||
*/
|
||||
jfs_err("jfs_dirty_inode called on read-only volume");
|
||||
jfs_err("Is remount racy?");
|
||||
noisy--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
set_cflag(COMMIT_Dirty, inode);
|
||||
}
|
||||
|
||||
int jfs_get_block(struct inode *ip, sector_t lblock,
|
||||
struct buffer_head *bh_result, int create)
|
||||
{
|
||||
s64 lblock64 = lblock;
|
||||
int rc = 0;
|
||||
xad_t xad;
|
||||
s64 xaddr;
|
||||
int xflag;
|
||||
s32 xlen = bh_result->b_size >> ip->i_blkbits;
|
||||
|
||||
/*
|
||||
* Take appropriate lock on inode
|
||||
*/
|
||||
if (create)
|
||||
IWRITE_LOCK(ip, RDWRLOCK_NORMAL);
|
||||
else
|
||||
IREAD_LOCK(ip, RDWRLOCK_NORMAL);
|
||||
|
||||
if (((lblock64 << ip->i_sb->s_blocksize_bits) < ip->i_size) &&
|
||||
(!xtLookup(ip, lblock64, xlen, &xflag, &xaddr, &xlen, 0)) &&
|
||||
xaddr) {
|
||||
if (xflag & XAD_NOTRECORDED) {
|
||||
if (!create)
|
||||
/*
|
||||
* Allocated but not recorded, read treats
|
||||
* this as a hole
|
||||
*/
|
||||
goto unlock;
|
||||
#ifdef _JFS_4K
|
||||
XADoffset(&xad, lblock64);
|
||||
XADlength(&xad, xlen);
|
||||
XADaddress(&xad, xaddr);
|
||||
#else /* _JFS_4K */
|
||||
/*
|
||||
* As long as block size = 4K, this isn't a problem.
|
||||
* We should mark the whole page not ABNR, but how
|
||||
* will we know to mark the other blocks BH_New?
|
||||
*/
|
||||
BUG();
|
||||
#endif /* _JFS_4K */
|
||||
rc = extRecord(ip, &xad);
|
||||
if (rc)
|
||||
goto unlock;
|
||||
set_buffer_new(bh_result);
|
||||
}
|
||||
|
||||
map_bh(bh_result, ip->i_sb, xaddr);
|
||||
bh_result->b_size = xlen << ip->i_blkbits;
|
||||
goto unlock;
|
||||
}
|
||||
if (!create)
|
||||
goto unlock;
|
||||
|
||||
/*
|
||||
* Allocate a new block
|
||||
*/
|
||||
#ifdef _JFS_4K
|
||||
if ((rc = extHint(ip, lblock64 << ip->i_sb->s_blocksize_bits, &xad)))
|
||||
goto unlock;
|
||||
rc = extAlloc(ip, xlen, lblock64, &xad, false);
|
||||
if (rc)
|
||||
goto unlock;
|
||||
|
||||
set_buffer_new(bh_result);
|
||||
map_bh(bh_result, ip->i_sb, addressXAD(&xad));
|
||||
bh_result->b_size = lengthXAD(&xad) << ip->i_blkbits;
|
||||
|
||||
#else /* _JFS_4K */
|
||||
/*
|
||||
* We need to do whatever it takes to keep all but the last buffers
|
||||
* in 4K pages - see jfs_write.c
|
||||
*/
|
||||
BUG();
|
||||
#endif /* _JFS_4K */
|
||||
|
||||
unlock:
|
||||
/*
|
||||
* Release lock on inode
|
||||
*/
|
||||
if (create)
|
||||
IWRITE_UNLOCK(ip);
|
||||
else
|
||||
IREAD_UNLOCK(ip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int jfs_writepage(struct page *page, struct writeback_control *wbc)
|
||||
{
|
||||
return block_write_full_page(page, jfs_get_block, wbc);
|
||||
}
|
||||
|
||||
static int jfs_writepages(struct address_space *mapping,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
return mpage_writepages(mapping, wbc, jfs_get_block);
|
||||
}
|
||||
|
||||
static int jfs_readpage(struct file *file, struct page *page)
|
||||
{
|
||||
return mpage_readpage(page, jfs_get_block);
|
||||
}
|
||||
|
||||
static int jfs_readpages(struct file *file, struct address_space *mapping,
|
||||
struct list_head *pages, unsigned nr_pages)
|
||||
{
|
||||
return mpage_readpages(mapping, pages, nr_pages, jfs_get_block);
|
||||
}
|
||||
|
||||
static void jfs_write_failed(struct address_space *mapping, loff_t to)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
|
||||
if (to > inode->i_size) {
|
||||
truncate_pagecache(inode, inode->i_size);
|
||||
jfs_truncate(inode);
|
||||
}
|
||||
}
|
||||
|
||||
static int jfs_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned flags,
|
||||
struct page **pagep, void **fsdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = nobh_write_begin(mapping, pos, len, flags, pagep, fsdata,
|
||||
jfs_get_block);
|
||||
if (unlikely(ret))
|
||||
jfs_write_failed(mapping, pos + len);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static sector_t jfs_bmap(struct address_space *mapping, sector_t block)
|
||||
{
|
||||
return generic_block_bmap(mapping, block, jfs_get_block);
|
||||
}
|
||||
|
||||
static ssize_t jfs_direct_IO(int rw, struct kiocb *iocb,
|
||||
struct iov_iter *iter, loff_t offset)
|
||||
{
|
||||
struct file *file = iocb->ki_filp;
|
||||
struct address_space *mapping = file->f_mapping;
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
size_t count = iov_iter_count(iter);
|
||||
ssize_t ret;
|
||||
|
||||
ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, jfs_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) && ret < 0)) {
|
||||
loff_t isize = i_size_read(inode);
|
||||
loff_t end = offset + count;
|
||||
|
||||
if (end > isize)
|
||||
jfs_write_failed(mapping, end);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct address_space_operations jfs_aops = {
|
||||
.readpage = jfs_readpage,
|
||||
.readpages = jfs_readpages,
|
||||
.writepage = jfs_writepage,
|
||||
.writepages = jfs_writepages,
|
||||
.write_begin = jfs_write_begin,
|
||||
.write_end = nobh_write_end,
|
||||
.bmap = jfs_bmap,
|
||||
.direct_IO = jfs_direct_IO,
|
||||
};
|
||||
|
||||
/*
|
||||
* Guts of jfs_truncate. Called with locks already held. Can be called
|
||||
* with directory for truncating directory index table.
|
||||
*/
|
||||
void jfs_truncate_nolock(struct inode *ip, loff_t length)
|
||||
{
|
||||
loff_t newsize;
|
||||
tid_t tid;
|
||||
|
||||
ASSERT(length >= 0);
|
||||
|
||||
if (test_cflag(COMMIT_Nolink, ip)) {
|
||||
xtTruncate(0, ip, length, COMMIT_WMAP);
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
tid = txBegin(ip->i_sb, 0);
|
||||
|
||||
/*
|
||||
* The commit_mutex cannot be taken before txBegin.
|
||||
* txBegin may block and there is a chance the inode
|
||||
* could be marked dirty and need to be committed
|
||||
* before txBegin unblocks
|
||||
*/
|
||||
mutex_lock(&JFS_IP(ip)->commit_mutex);
|
||||
|
||||
newsize = xtTruncate(tid, ip, length,
|
||||
COMMIT_TRUNCATE | COMMIT_PWMAP);
|
||||
if (newsize < 0) {
|
||||
txEnd(tid);
|
||||
mutex_unlock(&JFS_IP(ip)->commit_mutex);
|
||||
break;
|
||||
}
|
||||
|
||||
ip->i_mtime = ip->i_ctime = CURRENT_TIME;
|
||||
mark_inode_dirty(ip);
|
||||
|
||||
txCommit(tid, 1, &ip, 0);
|
||||
txEnd(tid);
|
||||
mutex_unlock(&JFS_IP(ip)->commit_mutex);
|
||||
} while (newsize > length); /* Truncate isn't always atomic */
|
||||
}
|
||||
|
||||
void jfs_truncate(struct inode *ip)
|
||||
{
|
||||
jfs_info("jfs_truncate: size = 0x%lx", (ulong) ip->i_size);
|
||||
|
||||
nobh_truncate_page(ip->i_mapping, ip->i_size, jfs_get_block);
|
||||
|
||||
IWRITE_LOCK(ip, RDWRLOCK_NORMAL);
|
||||
jfs_truncate_nolock(ip, ip->i_size);
|
||||
IWRITE_UNLOCK(ip);
|
||||
}
|
189
fs/jfs/ioctl.c
Normal file
189
fs/jfs/ioctl.c
Normal file
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* linux/fs/jfs/ioctl.c
|
||||
*
|
||||
* Copyright (C) 2006 Herbert Poetzl
|
||||
* adapted from Remy Card's ext2/ioctl.c
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <asm/current.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "jfs_filsys.h"
|
||||
#include "jfs_debug.h"
|
||||
#include "jfs_incore.h"
|
||||
#include "jfs_dinode.h"
|
||||
#include "jfs_inode.h"
|
||||
#include "jfs_dmap.h"
|
||||
#include "jfs_discard.h"
|
||||
|
||||
static struct {
|
||||
long jfs_flag;
|
||||
long ext2_flag;
|
||||
} jfs_map[] = {
|
||||
{JFS_NOATIME_FL, FS_NOATIME_FL},
|
||||
{JFS_DIRSYNC_FL, FS_DIRSYNC_FL},
|
||||
{JFS_SYNC_FL, FS_SYNC_FL},
|
||||
{JFS_SECRM_FL, FS_SECRM_FL},
|
||||
{JFS_UNRM_FL, FS_UNRM_FL},
|
||||
{JFS_APPEND_FL, FS_APPEND_FL},
|
||||
{JFS_IMMUTABLE_FL, FS_IMMUTABLE_FL},
|
||||
{0, 0},
|
||||
};
|
||||
|
||||
static long jfs_map_ext2(unsigned long flags, int from)
|
||||
{
|
||||
int index=0;
|
||||
long mapped=0;
|
||||
|
||||
while (jfs_map[index].jfs_flag) {
|
||||
if (from) {
|
||||
if (jfs_map[index].ext2_flag & flags)
|
||||
mapped |= jfs_map[index].jfs_flag;
|
||||
} else {
|
||||
if (jfs_map[index].jfs_flag & flags)
|
||||
mapped |= jfs_map[index].ext2_flag;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return mapped;
|
||||
}
|
||||
|
||||
|
||||
long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct jfs_inode_info *jfs_inode = JFS_IP(inode);
|
||||
unsigned int flags;
|
||||
|
||||
switch (cmd) {
|
||||
case JFS_IOC_GETFLAGS:
|
||||
jfs_get_inode_flags(jfs_inode);
|
||||
flags = jfs_inode->mode2 & JFS_FL_USER_VISIBLE;
|
||||
flags = jfs_map_ext2(flags, 0);
|
||||
return put_user(flags, (int __user *) arg);
|
||||
case JFS_IOC_SETFLAGS: {
|
||||
unsigned int oldflags;
|
||||
int err;
|
||||
|
||||
err = mnt_want_write_file(filp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!inode_owner_or_capable(inode)) {
|
||||
err = -EACCES;
|
||||
goto setflags_out;
|
||||
}
|
||||
if (get_user(flags, (int __user *) arg)) {
|
||||
err = -EFAULT;
|
||||
goto setflags_out;
|
||||
}
|
||||
|
||||
flags = jfs_map_ext2(flags, 1);
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
flags &= ~JFS_DIRSYNC_FL;
|
||||
|
||||
/* Is it quota file? Do not allow user to mess with it */
|
||||
if (IS_NOQUOTA(inode)) {
|
||||
err = -EPERM;
|
||||
goto setflags_out;
|
||||
}
|
||||
|
||||
/* Lock against other parallel changes of flags */
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
jfs_get_inode_flags(jfs_inode);
|
||||
oldflags = jfs_inode->mode2;
|
||||
|
||||
/*
|
||||
* The IMMUTABLE and APPEND_ONLY flags can only be changed by
|
||||
* the relevant capability.
|
||||
*/
|
||||
if ((oldflags & JFS_IMMUTABLE_FL) ||
|
||||
((flags ^ oldflags) &
|
||||
(JFS_APPEND_FL | JFS_IMMUTABLE_FL))) {
|
||||
if (!capable(CAP_LINUX_IMMUTABLE)) {
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
err = -EPERM;
|
||||
goto setflags_out;
|
||||
}
|
||||
}
|
||||
|
||||
flags = flags & JFS_FL_USER_MODIFIABLE;
|
||||
flags |= oldflags & ~JFS_FL_USER_MODIFIABLE;
|
||||
jfs_inode->mode2 = flags;
|
||||
|
||||
jfs_set_inode_flags(inode);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
inode->i_ctime = CURRENT_TIME_SEC;
|
||||
mark_inode_dirty(inode);
|
||||
setflags_out:
|
||||
mnt_drop_write_file(filp);
|
||||
return err;
|
||||
}
|
||||
|
||||
case FITRIM:
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct request_queue *q = bdev_get_queue(sb->s_bdev);
|
||||
struct fstrim_range range;
|
||||
s64 ret = 0;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (!blk_queue_discard(q)) {
|
||||
jfs_warn("FITRIM not supported on device");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (copy_from_user(&range, (struct fstrim_range __user *)arg,
|
||||
sizeof(range)))
|
||||
return -EFAULT;
|
||||
|
||||
range.minlen = max_t(unsigned int, range.minlen,
|
||||
q->limits.discard_granularity);
|
||||
|
||||
ret = jfs_ioc_trim(inode, &range);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (copy_to_user((struct fstrim_range __user *)arg, &range,
|
||||
sizeof(range)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
long jfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
/* While these ioctl numbers defined with 'long' and have different
|
||||
* numbers than the 64bit ABI,
|
||||
* the actual implementation only deals with ints and is compatible.
|
||||
*/
|
||||
switch (cmd) {
|
||||
case JFS_IOC_GETFLAGS32:
|
||||
cmd = JFS_IOC_GETFLAGS;
|
||||
break;
|
||||
case JFS_IOC_SETFLAGS32:
|
||||
cmd = JFS_IOC_SETFLAGS;
|
||||
break;
|
||||
case FITRIM:
|
||||
cmd = FITRIM;
|
||||
break;
|
||||
}
|
||||
return jfs_ioctl(filp, cmd, arg);
|
||||
}
|
||||
#endif
|
36
fs/jfs/jfs_acl.h
Normal file
36
fs/jfs/jfs_acl.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_ACL
|
||||
#define _H_JFS_ACL
|
||||
|
||||
#ifdef CONFIG_JFS_POSIX_ACL
|
||||
|
||||
struct posix_acl *jfs_get_acl(struct inode *inode, int type);
|
||||
int jfs_set_acl(struct inode *inode, struct posix_acl *acl, int type);
|
||||
int jfs_init_acl(tid_t, struct inode *, struct inode *);
|
||||
|
||||
#else
|
||||
|
||||
static inline int jfs_init_acl(tid_t tid, struct inode *inode,
|
||||
struct inode *dir)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* _H_JFS_ACL */
|
172
fs/jfs/jfs_btree.h
Normal file
172
fs/jfs/jfs_btree.h
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2004
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_BTREE
|
||||
#define _H_JFS_BTREE
|
||||
|
||||
/*
|
||||
* jfs_btree.h: B+-tree
|
||||
*
|
||||
* JFS B+-tree (dtree and xtree) common definitions
|
||||
*/
|
||||
|
||||
/*
|
||||
* basic btree page - btpage
|
||||
*
|
||||
struct btpage {
|
||||
s64 next; right sibling bn
|
||||
s64 prev; left sibling bn
|
||||
|
||||
u8 flag;
|
||||
u8 rsrvd[7]; type specific
|
||||
s64 self; self address
|
||||
|
||||
u8 entry[4064];
|
||||
}; */
|
||||
|
||||
/* btpaget_t flag */
|
||||
#define BT_TYPE 0x07 /* B+-tree index */
|
||||
#define BT_ROOT 0x01 /* root page */
|
||||
#define BT_LEAF 0x02 /* leaf page */
|
||||
#define BT_INTERNAL 0x04 /* internal page */
|
||||
#define BT_RIGHTMOST 0x10 /* rightmost page */
|
||||
#define BT_LEFTMOST 0x20 /* leftmost page */
|
||||
#define BT_SWAPPED 0x80 /* used by fsck for endian swapping */
|
||||
|
||||
/* btorder (in inode) */
|
||||
#define BT_RANDOM 0x0000
|
||||
#define BT_SEQUENTIAL 0x0001
|
||||
#define BT_LOOKUP 0x0010
|
||||
#define BT_INSERT 0x0020
|
||||
#define BT_DELETE 0x0040
|
||||
|
||||
/*
|
||||
* btree page buffer cache access
|
||||
*/
|
||||
#define BT_IS_ROOT(MP) (((MP)->xflag & COMMIT_PAGE) == 0)
|
||||
|
||||
/* get page from buffer page */
|
||||
#define BT_PAGE(IP, MP, TYPE, ROOT)\
|
||||
(BT_IS_ROOT(MP) ? (TYPE *)&JFS_IP(IP)->ROOT : (TYPE *)(MP)->data)
|
||||
|
||||
/* get the page buffer and the page for specified block address */
|
||||
#define BT_GETPAGE(IP, BN, MP, TYPE, SIZE, P, RC, ROOT)\
|
||||
{\
|
||||
if ((BN) == 0)\
|
||||
{\
|
||||
MP = (struct metapage *)&JFS_IP(IP)->bxflag;\
|
||||
P = (TYPE *)&JFS_IP(IP)->ROOT;\
|
||||
RC = 0;\
|
||||
}\
|
||||
else\
|
||||
{\
|
||||
MP = read_metapage((IP), BN, SIZE, 1);\
|
||||
if (MP) {\
|
||||
RC = 0;\
|
||||
P = (MP)->data;\
|
||||
} else {\
|
||||
P = NULL;\
|
||||
jfs_err("bread failed!");\
|
||||
RC = -EIO;\
|
||||
}\
|
||||
}\
|
||||
}
|
||||
|
||||
#define BT_MARK_DIRTY(MP, IP)\
|
||||
{\
|
||||
if (BT_IS_ROOT(MP))\
|
||||
mark_inode_dirty(IP);\
|
||||
else\
|
||||
mark_metapage_dirty(MP);\
|
||||
}
|
||||
|
||||
/* put the page buffer */
|
||||
#define BT_PUTPAGE(MP)\
|
||||
{\
|
||||
if (! BT_IS_ROOT(MP)) \
|
||||
release_metapage(MP); \
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* btree traversal stack
|
||||
*
|
||||
* record the path traversed during the search;
|
||||
* top frame record the leaf page/entry selected.
|
||||
*/
|
||||
struct btframe { /* stack frame */
|
||||
s64 bn; /* 8: */
|
||||
s16 index; /* 2: */
|
||||
s16 lastindex; /* 2: unused */
|
||||
struct metapage *mp; /* 4/8: */
|
||||
}; /* (16/24) */
|
||||
|
||||
struct btstack {
|
||||
struct btframe *top;
|
||||
int nsplit;
|
||||
struct btframe stack[MAXTREEHEIGHT];
|
||||
};
|
||||
|
||||
#define BT_CLR(btstack)\
|
||||
(btstack)->top = (btstack)->stack
|
||||
|
||||
#define BT_STACK_FULL(btstack)\
|
||||
( (btstack)->top == &((btstack)->stack[MAXTREEHEIGHT-1]))
|
||||
|
||||
#define BT_PUSH(BTSTACK, BN, INDEX)\
|
||||
{\
|
||||
assert(!BT_STACK_FULL(BTSTACK));\
|
||||
(BTSTACK)->top->bn = BN;\
|
||||
(BTSTACK)->top->index = INDEX;\
|
||||
++(BTSTACK)->top;\
|
||||
}
|
||||
|
||||
#define BT_POP(btstack)\
|
||||
( (btstack)->top == (btstack)->stack ? NULL : --(btstack)->top )
|
||||
|
||||
#define BT_STACK(btstack)\
|
||||
( (btstack)->top == (btstack)->stack ? NULL : (btstack)->top )
|
||||
|
||||
static inline void BT_STACK_DUMP(struct btstack *btstack)
|
||||
{
|
||||
int i;
|
||||
printk("btstack dump:\n");
|
||||
for (i = 0; i < MAXTREEHEIGHT; i++)
|
||||
printk(KERN_ERR "bn = %Lx, index = %d\n",
|
||||
(long long)btstack->stack[i].bn,
|
||||
btstack->stack[i].index);
|
||||
}
|
||||
|
||||
/* retrieve search results */
|
||||
#define BT_GETSEARCH(IP, LEAF, BN, MP, TYPE, P, INDEX, ROOT)\
|
||||
{\
|
||||
BN = (LEAF)->bn;\
|
||||
MP = (LEAF)->mp;\
|
||||
if (BN)\
|
||||
P = (TYPE *)MP->data;\
|
||||
else\
|
||||
P = (TYPE *)&JFS_IP(IP)->ROOT;\
|
||||
INDEX = (LEAF)->index;\
|
||||
}
|
||||
|
||||
/* put the page buffer of search */
|
||||
#define BT_PUTSEARCH(BTSTACK)\
|
||||
{\
|
||||
if (! BT_IS_ROOT((BTSTACK)->top->mp))\
|
||||
release_metapage((BTSTACK)->top->mp);\
|
||||
}
|
||||
#endif /* _H_JFS_BTREE */
|
109
fs/jfs/jfs_debug.c
Normal file
109
fs/jfs/jfs_debug.c
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2004
|
||||
* Portions Copyright (C) Christoph Hellwig, 2001-2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "jfs_incore.h"
|
||||
#include "jfs_filsys.h"
|
||||
#include "jfs_debug.h"
|
||||
|
||||
#ifdef PROC_FS_JFS /* see jfs_debug.h */
|
||||
|
||||
static struct proc_dir_entry *base;
|
||||
#ifdef CONFIG_JFS_DEBUG
|
||||
static int jfs_loglevel_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
seq_printf(m, "%d\n", jfsloglevel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jfs_loglevel_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, jfs_loglevel_proc_show, NULL);
|
||||
}
|
||||
|
||||
static ssize_t jfs_loglevel_proc_write(struct file *file,
|
||||
const char __user *buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
char c;
|
||||
|
||||
if (get_user(c, buffer))
|
||||
return -EFAULT;
|
||||
|
||||
/* yes, I know this is an ASCIIism. --hch */
|
||||
if (c < '0' || c > '9')
|
||||
return -EINVAL;
|
||||
jfsloglevel = c - '0';
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations jfs_loglevel_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = jfs_loglevel_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.write = jfs_loglevel_proc_write,
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct {
|
||||
const char *name;
|
||||
const struct file_operations *proc_fops;
|
||||
} Entries[] = {
|
||||
#ifdef CONFIG_JFS_STATISTICS
|
||||
{ "lmstats", &jfs_lmstats_proc_fops, },
|
||||
{ "txstats", &jfs_txstats_proc_fops, },
|
||||
{ "xtstat", &jfs_xtstat_proc_fops, },
|
||||
{ "mpstat", &jfs_mpstat_proc_fops, },
|
||||
#endif
|
||||
#ifdef CONFIG_JFS_DEBUG
|
||||
{ "TxAnchor", &jfs_txanchor_proc_fops, },
|
||||
{ "loglevel", &jfs_loglevel_proc_fops }
|
||||
#endif
|
||||
};
|
||||
#define NPROCENT ARRAY_SIZE(Entries)
|
||||
|
||||
void jfs_proc_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!(base = proc_mkdir("fs/jfs", NULL)))
|
||||
return;
|
||||
|
||||
for (i = 0; i < NPROCENT; i++)
|
||||
proc_create(Entries[i].name, 0, base, Entries[i].proc_fops);
|
||||
}
|
||||
|
||||
void jfs_proc_clean(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (base) {
|
||||
for (i = 0; i < NPROCENT; i++)
|
||||
remove_proc_entry(Entries[i].name, base);
|
||||
remove_proc_entry("fs/jfs", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* PROC_FS_JFS */
|
122
fs/jfs/jfs_debug.h
Normal file
122
fs/jfs/jfs_debug.h
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2002
|
||||
* Portions Copyright (C) Christoph Hellwig, 2001-2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_DEBUG
|
||||
#define _H_JFS_DEBUG
|
||||
|
||||
/*
|
||||
* jfs_debug.h
|
||||
*
|
||||
* global debug message, data structure/macro definitions
|
||||
* under control of CONFIG_JFS_DEBUG, CONFIG_JFS_STATISTICS;
|
||||
*/
|
||||
|
||||
/*
|
||||
* Create /proc/fs/jfs if procfs is enabled andeither
|
||||
* CONFIG_JFS_DEBUG or CONFIG_JFS_STATISTICS is defined
|
||||
*/
|
||||
#if defined(CONFIG_PROC_FS) && (defined(CONFIG_JFS_DEBUG) || defined(CONFIG_JFS_STATISTICS))
|
||||
#define PROC_FS_JFS
|
||||
extern void jfs_proc_init(void);
|
||||
extern void jfs_proc_clean(void);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* assert with traditional printf/panic
|
||||
*/
|
||||
#define assert(p) do { \
|
||||
if (!(p)) { \
|
||||
printk(KERN_CRIT "BUG at %s:%d assert(%s)\n", \
|
||||
__FILE__, __LINE__, #p); \
|
||||
BUG(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* debug ON
|
||||
* --------
|
||||
*/
|
||||
#ifdef CONFIG_JFS_DEBUG
|
||||
#define ASSERT(p) assert(p)
|
||||
|
||||
/* printk verbosity */
|
||||
#define JFS_LOGLEVEL_ERR 1
|
||||
#define JFS_LOGLEVEL_WARN 2
|
||||
#define JFS_LOGLEVEL_DEBUG 3
|
||||
#define JFS_LOGLEVEL_INFO 4
|
||||
|
||||
extern int jfsloglevel;
|
||||
|
||||
extern const struct file_operations jfs_txanchor_proc_fops;
|
||||
|
||||
/* information message: e.g., configuration, major event */
|
||||
#define jfs_info(fmt, arg...) do { \
|
||||
if (jfsloglevel >= JFS_LOGLEVEL_INFO) \
|
||||
printk(KERN_INFO fmt "\n", ## arg); \
|
||||
} while (0)
|
||||
|
||||
/* debug message: ad hoc */
|
||||
#define jfs_debug(fmt, arg...) do { \
|
||||
if (jfsloglevel >= JFS_LOGLEVEL_DEBUG) \
|
||||
printk(KERN_DEBUG fmt "\n", ## arg); \
|
||||
} while (0)
|
||||
|
||||
/* warn message: */
|
||||
#define jfs_warn(fmt, arg...) do { \
|
||||
if (jfsloglevel >= JFS_LOGLEVEL_WARN) \
|
||||
printk(KERN_WARNING fmt "\n", ## arg); \
|
||||
} while (0)
|
||||
|
||||
/* error event message: e.g., i/o error */
|
||||
#define jfs_err(fmt, arg...) do { \
|
||||
if (jfsloglevel >= JFS_LOGLEVEL_ERR) \
|
||||
printk(KERN_ERR fmt "\n", ## arg); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* debug OFF
|
||||
* ---------
|
||||
*/
|
||||
#else /* CONFIG_JFS_DEBUG */
|
||||
#define ASSERT(p) do {} while (0)
|
||||
#define jfs_info(fmt, arg...) do {} while (0)
|
||||
#define jfs_debug(fmt, arg...) do {} while (0)
|
||||
#define jfs_warn(fmt, arg...) do {} while (0)
|
||||
#define jfs_err(fmt, arg...) do {} while (0)
|
||||
#endif /* CONFIG_JFS_DEBUG */
|
||||
|
||||
/*
|
||||
* statistics
|
||||
* ----------
|
||||
*/
|
||||
#ifdef CONFIG_JFS_STATISTICS
|
||||
extern const struct file_operations jfs_lmstats_proc_fops;
|
||||
extern const struct file_operations jfs_txstats_proc_fops;
|
||||
extern const struct file_operations jfs_mpstat_proc_fops;
|
||||
extern const struct file_operations jfs_xtstat_proc_fops;
|
||||
|
||||
#define INCREMENT(x) ((x)++)
|
||||
#define DECREMENT(x) ((x)--)
|
||||
#define HIGHWATERMARK(x,y) ((x) = max((x), (y)))
|
||||
#else
|
||||
#define INCREMENT(x)
|
||||
#define DECREMENT(x)
|
||||
#define HIGHWATERMARK(x,y)
|
||||
#endif /* CONFIG_JFS_STATISTICS */
|
||||
|
||||
#endif /* _H_JFS_DEBUG */
|
176
fs/jfs/jfs_dinode.h
Normal file
176
fs/jfs/jfs_dinode.h
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2001
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_DINODE
|
||||
#define _H_JFS_DINODE
|
||||
|
||||
/*
|
||||
* jfs_dinode.h: on-disk inode manager
|
||||
*/
|
||||
|
||||
#define INODESLOTSIZE 128
|
||||
#define L2INODESLOTSIZE 7
|
||||
#define log2INODESIZE 9 /* log2(bytes per dinode) */
|
||||
|
||||
|
||||
/*
|
||||
* on-disk inode : 512 bytes
|
||||
*
|
||||
* note: align 64-bit fields on 8-byte boundary.
|
||||
*/
|
||||
struct dinode {
|
||||
/*
|
||||
* I. base area (128 bytes)
|
||||
* ------------------------
|
||||
*
|
||||
* define generic/POSIX attributes
|
||||
*/
|
||||
__le32 di_inostamp; /* 4: stamp to show inode belongs to fileset */
|
||||
__le32 di_fileset; /* 4: fileset number */
|
||||
__le32 di_number; /* 4: inode number, aka file serial number */
|
||||
__le32 di_gen; /* 4: inode generation number */
|
||||
|
||||
pxd_t di_ixpxd; /* 8: inode extent descriptor */
|
||||
|
||||
__le64 di_size; /* 8: size */
|
||||
__le64 di_nblocks; /* 8: number of blocks allocated */
|
||||
|
||||
__le32 di_nlink; /* 4: number of links to the object */
|
||||
|
||||
__le32 di_uid; /* 4: user id of owner */
|
||||
__le32 di_gid; /* 4: group id of owner */
|
||||
|
||||
__le32 di_mode; /* 4: attribute, format and permission */
|
||||
|
||||
struct timestruc_t di_atime; /* 8: time last data accessed */
|
||||
struct timestruc_t di_ctime; /* 8: time last status changed */
|
||||
struct timestruc_t di_mtime; /* 8: time last data modified */
|
||||
struct timestruc_t di_otime; /* 8: time created */
|
||||
|
||||
dxd_t di_acl; /* 16: acl descriptor */
|
||||
|
||||
dxd_t di_ea; /* 16: ea descriptor */
|
||||
|
||||
__le32 di_next_index; /* 4: Next available dir_table index */
|
||||
|
||||
__le32 di_acltype; /* 4: Type of ACL */
|
||||
|
||||
/*
|
||||
* Extension Areas.
|
||||
*
|
||||
* Historically, the inode was partitioned into 4 128-byte areas,
|
||||
* the last 3 being defined as unions which could have multiple
|
||||
* uses. The first 96 bytes had been completely unused until
|
||||
* an index table was added to the directory. It is now more
|
||||
* useful to describe the last 3/4 of the inode as a single
|
||||
* union. We would probably be better off redesigning the
|
||||
* entire structure from scratch, but we don't want to break
|
||||
* commonality with OS/2's JFS at this time.
|
||||
*/
|
||||
union {
|
||||
struct {
|
||||
/*
|
||||
* This table contains the information needed to
|
||||
* find a directory entry from a 32-bit index.
|
||||
* If the index is small enough, the table is inline,
|
||||
* otherwise, an x-tree root overlays this table
|
||||
*/
|
||||
struct dir_table_slot _table[12]; /* 96: inline */
|
||||
|
||||
dtroot_t _dtroot; /* 288: dtree root */
|
||||
} _dir; /* (384) */
|
||||
#define di_dirtable u._dir._table
|
||||
#define di_dtroot u._dir._dtroot
|
||||
#define di_parent di_dtroot.header.idotdot
|
||||
#define di_DASD di_dtroot.header.DASD
|
||||
|
||||
struct {
|
||||
union {
|
||||
u8 _data[96]; /* 96: unused */
|
||||
struct {
|
||||
void *_imap; /* 4: unused */
|
||||
__le32 _gengen; /* 4: generator */
|
||||
} _imap;
|
||||
} _u1; /* 96: */
|
||||
#define di_gengen u._file._u1._imap._gengen
|
||||
|
||||
union {
|
||||
xtpage_t _xtroot;
|
||||
struct {
|
||||
u8 unused[16]; /* 16: */
|
||||
dxd_t _dxd; /* 16: */
|
||||
union {
|
||||
__le32 _rdev; /* 4: */
|
||||
u8 _fastsymlink[128];
|
||||
} _u;
|
||||
u8 _inlineea[128];
|
||||
} _special;
|
||||
} _u2;
|
||||
} _file;
|
||||
#define di_xtroot u._file._u2._xtroot
|
||||
#define di_dxd u._file._u2._special._dxd
|
||||
#define di_btroot di_xtroot
|
||||
#define di_inlinedata u._file._u2._special._u
|
||||
#define di_rdev u._file._u2._special._u._rdev
|
||||
#define di_fastsymlink u._file._u2._special._u._fastsymlink
|
||||
#define di_inlineea u._file._u2._special._inlineea
|
||||
} u;
|
||||
};
|
||||
|
||||
/* extended mode bits (on-disk inode di_mode) */
|
||||
#define IFJOURNAL 0x00010000 /* journalled file */
|
||||
#define ISPARSE 0x00020000 /* sparse file enabled */
|
||||
#define INLINEEA 0x00040000 /* inline EA area free */
|
||||
#define ISWAPFILE 0x00800000 /* file open for pager swap space */
|
||||
|
||||
/* more extended mode bits: attributes for OS/2 */
|
||||
#define IREADONLY 0x02000000 /* no write access to file */
|
||||
#define IHIDDEN 0x04000000 /* hidden file */
|
||||
#define ISYSTEM 0x08000000 /* system file */
|
||||
|
||||
#define IDIRECTORY 0x20000000 /* directory (shadow of real bit) */
|
||||
#define IARCHIVE 0x40000000 /* file archive bit */
|
||||
#define INEWNAME 0x80000000 /* non-8.3 filename format */
|
||||
|
||||
#define IRASH 0x4E000000 /* mask for changeable attributes */
|
||||
#define ATTRSHIFT 25 /* bits to shift to move attribute
|
||||
specification to mode position */
|
||||
|
||||
/* extended attributes for Linux */
|
||||
|
||||
#define JFS_NOATIME_FL 0x00080000 /* do not update atime */
|
||||
|
||||
#define JFS_DIRSYNC_FL 0x00100000 /* dirsync behaviour */
|
||||
#define JFS_SYNC_FL 0x00200000 /* Synchronous updates */
|
||||
#define JFS_SECRM_FL 0x00400000 /* Secure deletion */
|
||||
#define JFS_UNRM_FL 0x00800000 /* allow for undelete */
|
||||
|
||||
#define JFS_APPEND_FL 0x01000000 /* writes to file may only append */
|
||||
#define JFS_IMMUTABLE_FL 0x02000000 /* Immutable file */
|
||||
|
||||
#define JFS_FL_USER_VISIBLE 0x03F80000
|
||||
#define JFS_FL_USER_MODIFIABLE 0x03F80000
|
||||
#define JFS_FL_INHERIT 0x03C80000
|
||||
|
||||
/* These are identical to EXT[23]_IOC_GETFLAGS/SETFLAGS */
|
||||
#define JFS_IOC_GETFLAGS _IOR('f', 1, long)
|
||||
#define JFS_IOC_SETFLAGS _IOW('f', 2, long)
|
||||
|
||||
#define JFS_IOC_GETFLAGS32 _IOR('f', 1, int)
|
||||
#define JFS_IOC_SETFLAGS32 _IOW('f', 2, int)
|
||||
|
||||
#endif /*_H_JFS_DINODE */
|
121
fs/jfs/jfs_discard.c
Normal file
121
fs/jfs/jfs_discard.c
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (C) Tino Reichardt, 2012
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/blkdev.h>
|
||||
|
||||
#include "jfs_incore.h"
|
||||
#include "jfs_superblock.h"
|
||||
#include "jfs_discard.h"
|
||||
#include "jfs_dmap.h"
|
||||
#include "jfs_debug.h"
|
||||
|
||||
|
||||
/*
|
||||
* NAME: jfs_issue_discard()
|
||||
*
|
||||
* FUNCTION: TRIM the specified block range on device, if supported
|
||||
*
|
||||
* PARAMETERS:
|
||||
* ip - pointer to in-core inode
|
||||
* blkno - starting block number to be trimmed (0..N)
|
||||
* nblocks - number of blocks to be trimmed
|
||||
*
|
||||
* RETURN VALUES:
|
||||
* none
|
||||
*
|
||||
* serialization: IREAD_LOCK(ipbmap) held on entry/exit;
|
||||
*/
|
||||
void jfs_issue_discard(struct inode *ip, u64 blkno, u64 nblocks)
|
||||
{
|
||||
struct super_block *sb = ip->i_sb;
|
||||
int r = 0;
|
||||
|
||||
r = sb_issue_discard(sb, blkno, nblocks, GFP_NOFS, 0);
|
||||
if (unlikely(r != 0)) {
|
||||
jfs_err("JFS: sb_issue_discard" \
|
||||
"(%p, %llu, %llu, GFP_NOFS, 0) = %d => failed!\n",
|
||||
sb, (unsigned long long)blkno,
|
||||
(unsigned long long)nblocks, r);
|
||||
}
|
||||
|
||||
jfs_info("JFS: sb_issue_discard" \
|
||||
"(%p, %llu, %llu, GFP_NOFS, 0) = %d\n",
|
||||
sb, (unsigned long long)blkno,
|
||||
(unsigned long long)nblocks, r);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: jfs_ioc_trim()
|
||||
*
|
||||
* FUNCTION: attempt to discard (TRIM) all free blocks from the
|
||||
* filesystem.
|
||||
*
|
||||
* PARAMETERS:
|
||||
* ip - pointer to in-core inode;
|
||||
* range - the range, given by user space
|
||||
*
|
||||
* RETURN VALUES:
|
||||
* 0 - success
|
||||
* -EIO - i/o error
|
||||
*/
|
||||
int jfs_ioc_trim(struct inode *ip, struct fstrim_range *range)
|
||||
{
|
||||
struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
|
||||
struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap;
|
||||
struct super_block *sb = ipbmap->i_sb;
|
||||
int agno, agno_end;
|
||||
u64 start, end, minlen;
|
||||
u64 trimmed = 0;
|
||||
|
||||
/**
|
||||
* convert byte values to block size of filesystem:
|
||||
* start: First Byte to trim
|
||||
* len: number of Bytes to trim from start
|
||||
* minlen: minimum extent length in Bytes
|
||||
*/
|
||||
start = range->start >> sb->s_blocksize_bits;
|
||||
end = start + (range->len >> sb->s_blocksize_bits) - 1;
|
||||
minlen = range->minlen >> sb->s_blocksize_bits;
|
||||
if (minlen == 0)
|
||||
minlen = 1;
|
||||
|
||||
if (minlen > bmp->db_agsize ||
|
||||
start >= bmp->db_mapsize ||
|
||||
range->len < sb->s_blocksize)
|
||||
return -EINVAL;
|
||||
|
||||
if (end >= bmp->db_mapsize)
|
||||
end = bmp->db_mapsize - 1;
|
||||
|
||||
/**
|
||||
* we trim all ag's within the range
|
||||
*/
|
||||
agno = BLKTOAG(start, JFS_SBI(ip->i_sb));
|
||||
agno_end = BLKTOAG(end, JFS_SBI(ip->i_sb));
|
||||
while (agno <= agno_end) {
|
||||
trimmed += dbDiscardAG(ip, agno, minlen);
|
||||
agno++;
|
||||
}
|
||||
range->len = trimmed << sb->s_blocksize_bits;
|
||||
|
||||
return 0;
|
||||
}
|
26
fs/jfs/jfs_discard.h
Normal file
26
fs/jfs/jfs_discard.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (C) Tino Reichardt, 2012
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_DISCARD
|
||||
#define _H_JFS_DISCARD
|
||||
|
||||
struct fstrim_range;
|
||||
|
||||
extern void jfs_issue_discard(struct inode *ip, u64 blkno, u64 nblocks);
|
||||
extern int jfs_ioc_trim(struct inode *ip, struct fstrim_range *range);
|
||||
|
||||
#endif /* _H_JFS_DISCARD */
|
4092
fs/jfs/jfs_dmap.c
Normal file
4092
fs/jfs/jfs_dmap.c
Normal file
File diff suppressed because it is too large
Load diff
316
fs/jfs/jfs_dmap.h
Normal file
316
fs/jfs/jfs_dmap.h
Normal file
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_DMAP
|
||||
#define _H_JFS_DMAP
|
||||
|
||||
#include "jfs_txnmgr.h"
|
||||
|
||||
#define BMAPVERSION 1 /* version number */
|
||||
#define TREESIZE (256+64+16+4+1) /* size of a dmap tree */
|
||||
#define LEAFIND (64+16+4+1) /* index of 1st leaf of a dmap tree */
|
||||
#define LPERDMAP 256 /* num leaves per dmap tree */
|
||||
#define L2LPERDMAP 8 /* l2 number of leaves per dmap tree */
|
||||
#define DBWORD 32 /* # of blks covered by a map word */
|
||||
#define L2DBWORD 5 /* l2 # of blks covered by a mword */
|
||||
#define BUDMIN L2DBWORD /* max free string in a map word */
|
||||
#define BPERDMAP (LPERDMAP * DBWORD) /* num of blks per dmap */
|
||||
#define L2BPERDMAP 13 /* l2 num of blks per dmap */
|
||||
#define CTLTREESIZE (1024+256+64+16+4+1) /* size of a dmapctl tree */
|
||||
#define CTLLEAFIND (256+64+16+4+1) /* idx of 1st leaf of a dmapctl tree */
|
||||
#define LPERCTL 1024 /* num of leaves per dmapctl tree */
|
||||
#define L2LPERCTL 10 /* l2 num of leaves per dmapctl tree */
|
||||
#define ROOT 0 /* index of the root of a tree */
|
||||
#define NOFREE ((s8) -1) /* no blocks free */
|
||||
#define MAXAG 128 /* max number of allocation groups */
|
||||
#define L2MAXAG 7 /* l2 max num of AG */
|
||||
#define L2MINAGSZ 25 /* l2 of minimum AG size in bytes */
|
||||
#define BMAPBLKNO 0 /* lblkno of bmap within the map */
|
||||
|
||||
/*
|
||||
* maximum l2 number of disk blocks at the various dmapctl levels.
|
||||
*/
|
||||
#define L2MAXL0SIZE (L2BPERDMAP + 1 * L2LPERCTL)
|
||||
#define L2MAXL1SIZE (L2BPERDMAP + 2 * L2LPERCTL)
|
||||
#define L2MAXL2SIZE (L2BPERDMAP + 3 * L2LPERCTL)
|
||||
|
||||
/*
|
||||
* maximum number of disk blocks at the various dmapctl levels.
|
||||
*/
|
||||
#define MAXL0SIZE ((s64)1 << L2MAXL0SIZE)
|
||||
#define MAXL1SIZE ((s64)1 << L2MAXL1SIZE)
|
||||
#define MAXL2SIZE ((s64)1 << L2MAXL2SIZE)
|
||||
|
||||
#define MAXMAPSIZE MAXL2SIZE /* maximum aggregate map size */
|
||||
|
||||
/*
|
||||
* determine the maximum free string for four (lower level) nodes
|
||||
* of the tree.
|
||||
*/
|
||||
static inline signed char TREEMAX(signed char *cp)
|
||||
{
|
||||
signed char tmp1, tmp2;
|
||||
|
||||
tmp1 = max(*(cp+2), *(cp+3));
|
||||
tmp2 = max(*(cp), *(cp+1));
|
||||
|
||||
return max(tmp1, tmp2);
|
||||
}
|
||||
|
||||
/*
|
||||
* convert disk block number to the logical block number of the dmap
|
||||
* describing the disk block. s is the log2(number of logical blocks per page)
|
||||
*
|
||||
* The calculation figures out how many logical pages are in front of the dmap.
|
||||
* - the number of dmaps preceding it
|
||||
* - the number of L0 pages preceding its L0 page
|
||||
* - the number of L1 pages preceding its L1 page
|
||||
* - 3 is added to account for the L2, L1, and L0 page for this dmap
|
||||
* - 1 is added to account for the control page of the map.
|
||||
*/
|
||||
#define BLKTODMAP(b,s) \
|
||||
((((b) >> 13) + ((b) >> 23) + ((b) >> 33) + 3 + 1) << (s))
|
||||
|
||||
/*
|
||||
* convert disk block number to the logical block number of the LEVEL 0
|
||||
* dmapctl describing the disk block. s is the log2(number of logical blocks
|
||||
* per page)
|
||||
*
|
||||
* The calculation figures out how many logical pages are in front of the L0.
|
||||
* - the number of dmap pages preceding it
|
||||
* - the number of L0 pages preceding it
|
||||
* - the number of L1 pages preceding its L1 page
|
||||
* - 2 is added to account for the L2, and L1 page for this L0
|
||||
* - 1 is added to account for the control page of the map.
|
||||
*/
|
||||
#define BLKTOL0(b,s) \
|
||||
(((((b) >> 23) << 10) + ((b) >> 23) + ((b) >> 33) + 2 + 1) << (s))
|
||||
|
||||
/*
|
||||
* convert disk block number to the logical block number of the LEVEL 1
|
||||
* dmapctl describing the disk block. s is the log2(number of logical blocks
|
||||
* per page)
|
||||
*
|
||||
* The calculation figures out how many logical pages are in front of the L1.
|
||||
* - the number of dmap pages preceding it
|
||||
* - the number of L0 pages preceding it
|
||||
* - the number of L1 pages preceding it
|
||||
* - 1 is added to account for the L2 page
|
||||
* - 1 is added to account for the control page of the map.
|
||||
*/
|
||||
#define BLKTOL1(b,s) \
|
||||
(((((b) >> 33) << 20) + (((b) >> 33) << 10) + ((b) >> 33) + 1 + 1) << (s))
|
||||
|
||||
/*
|
||||
* convert disk block number to the logical block number of the dmapctl
|
||||
* at the specified level which describes the disk block.
|
||||
*/
|
||||
#define BLKTOCTL(b,s,l) \
|
||||
(((l) == 2) ? 1 : ((l) == 1) ? BLKTOL1((b),(s)) : BLKTOL0((b),(s)))
|
||||
|
||||
/*
|
||||
* convert aggregate map size to the zero origin dmapctl level of the
|
||||
* top dmapctl.
|
||||
*/
|
||||
#define BMAPSZTOLEV(size) \
|
||||
(((size) <= MAXL0SIZE) ? 0 : ((size) <= MAXL1SIZE) ? 1 : 2)
|
||||
|
||||
/* convert disk block number to allocation group number.
|
||||
*/
|
||||
#define BLKTOAG(b,sbi) ((b) >> ((sbi)->bmap->db_agl2size))
|
||||
|
||||
/* convert allocation group number to starting disk block
|
||||
* number.
|
||||
*/
|
||||
#define AGTOBLK(a,ip) \
|
||||
((s64)(a) << (JFS_SBI((ip)->i_sb)->bmap->db_agl2size))
|
||||
|
||||
/*
|
||||
* dmap summary tree
|
||||
*
|
||||
* dmaptree must be consistent with dmapctl.
|
||||
*/
|
||||
struct dmaptree {
|
||||
__le32 nleafs; /* 4: number of tree leafs */
|
||||
__le32 l2nleafs; /* 4: l2 number of tree leafs */
|
||||
__le32 leafidx; /* 4: index of first tree leaf */
|
||||
__le32 height; /* 4: height of the tree */
|
||||
s8 budmin; /* 1: min l2 tree leaf value to combine */
|
||||
s8 stree[TREESIZE]; /* TREESIZE: tree */
|
||||
u8 pad[2]; /* 2: pad to word boundary */
|
||||
}; /* - 360 - */
|
||||
|
||||
/*
|
||||
* dmap page per 8K blocks bitmap
|
||||
*/
|
||||
struct dmap {
|
||||
__le32 nblocks; /* 4: num blks covered by this dmap */
|
||||
__le32 nfree; /* 4: num of free blks in this dmap */
|
||||
__le64 start; /* 8: starting blkno for this dmap */
|
||||
struct dmaptree tree; /* 360: dmap tree */
|
||||
u8 pad[1672]; /* 1672: pad to 2048 bytes */
|
||||
__le32 wmap[LPERDMAP]; /* 1024: bits of the working map */
|
||||
__le32 pmap[LPERDMAP]; /* 1024: bits of the persistent map */
|
||||
}; /* - 4096 - */
|
||||
|
||||
/*
|
||||
* disk map control page per level.
|
||||
*
|
||||
* dmapctl must be consistent with dmaptree.
|
||||
*/
|
||||
struct dmapctl {
|
||||
__le32 nleafs; /* 4: number of tree leafs */
|
||||
__le32 l2nleafs; /* 4: l2 number of tree leafs */
|
||||
__le32 leafidx; /* 4: index of the first tree leaf */
|
||||
__le32 height; /* 4: height of tree */
|
||||
s8 budmin; /* 1: minimum l2 tree leaf value */
|
||||
s8 stree[CTLTREESIZE]; /* CTLTREESIZE: dmapctl tree */
|
||||
u8 pad[2714]; /* 2714: pad to 4096 */
|
||||
}; /* - 4096 - */
|
||||
|
||||
/*
|
||||
* common definition for dmaptree within dmap and dmapctl
|
||||
*/
|
||||
typedef union dmtree {
|
||||
struct dmaptree t1;
|
||||
struct dmapctl t2;
|
||||
} dmtree_t;
|
||||
|
||||
/* macros for accessing fields within dmtree */
|
||||
#define dmt_nleafs t1.nleafs
|
||||
#define dmt_l2nleafs t1.l2nleafs
|
||||
#define dmt_leafidx t1.leafidx
|
||||
#define dmt_height t1.height
|
||||
#define dmt_budmin t1.budmin
|
||||
#define dmt_stree t1.stree
|
||||
|
||||
/*
|
||||
* on-disk aggregate disk allocation map descriptor.
|
||||
*/
|
||||
struct dbmap_disk {
|
||||
__le64 dn_mapsize; /* 8: number of blocks in aggregate */
|
||||
__le64 dn_nfree; /* 8: num free blks in aggregate map */
|
||||
__le32 dn_l2nbperpage; /* 4: number of blks per page */
|
||||
__le32 dn_numag; /* 4: total number of ags */
|
||||
__le32 dn_maxlevel; /* 4: number of active ags */
|
||||
__le32 dn_maxag; /* 4: max active alloc group number */
|
||||
__le32 dn_agpref; /* 4: preferred alloc group (hint) */
|
||||
__le32 dn_aglevel; /* 4: dmapctl level holding the AG */
|
||||
__le32 dn_agheight; /* 4: height in dmapctl of the AG */
|
||||
__le32 dn_agwidth; /* 4: width in dmapctl of the AG */
|
||||
__le32 dn_agstart; /* 4: start tree index at AG height */
|
||||
__le32 dn_agl2size; /* 4: l2 num of blks per alloc group */
|
||||
__le64 dn_agfree[MAXAG];/* 8*MAXAG: per AG free count */
|
||||
__le64 dn_agsize; /* 8: num of blks per alloc group */
|
||||
s8 dn_maxfreebud; /* 1: max free buddy system */
|
||||
u8 pad[3007]; /* 3007: pad to 4096 */
|
||||
}; /* - 4096 - */
|
||||
|
||||
struct dbmap {
|
||||
s64 dn_mapsize; /* number of blocks in aggregate */
|
||||
s64 dn_nfree; /* num free blks in aggregate map */
|
||||
int dn_l2nbperpage; /* number of blks per page */
|
||||
int dn_numag; /* total number of ags */
|
||||
int dn_maxlevel; /* number of active ags */
|
||||
int dn_maxag; /* max active alloc group number */
|
||||
int dn_agpref; /* preferred alloc group (hint) */
|
||||
int dn_aglevel; /* dmapctl level holding the AG */
|
||||
int dn_agheight; /* height in dmapctl of the AG */
|
||||
int dn_agwidth; /* width in dmapctl of the AG */
|
||||
int dn_agstart; /* start tree index at AG height */
|
||||
int dn_agl2size; /* l2 num of blks per alloc group */
|
||||
s64 dn_agfree[MAXAG]; /* per AG free count */
|
||||
s64 dn_agsize; /* num of blks per alloc group */
|
||||
signed char dn_maxfreebud; /* max free buddy system */
|
||||
}; /* - 4096 - */
|
||||
/*
|
||||
* in-memory aggregate disk allocation map descriptor.
|
||||
*/
|
||||
struct bmap {
|
||||
struct dbmap db_bmap; /* on-disk aggregate map descriptor */
|
||||
struct inode *db_ipbmap; /* ptr to aggregate map incore inode */
|
||||
struct mutex db_bmaplock; /* aggregate map lock */
|
||||
atomic_t db_active[MAXAG]; /* count of active, open files in AG */
|
||||
u32 *db_DBmap;
|
||||
};
|
||||
|
||||
/* macros for accessing fields within in-memory aggregate map descriptor */
|
||||
#define db_mapsize db_bmap.dn_mapsize
|
||||
#define db_nfree db_bmap.dn_nfree
|
||||
#define db_agfree db_bmap.dn_agfree
|
||||
#define db_agsize db_bmap.dn_agsize
|
||||
#define db_agl2size db_bmap.dn_agl2size
|
||||
#define db_agwidth db_bmap.dn_agwidth
|
||||
#define db_agheight db_bmap.dn_agheight
|
||||
#define db_agstart db_bmap.dn_agstart
|
||||
#define db_numag db_bmap.dn_numag
|
||||
#define db_maxlevel db_bmap.dn_maxlevel
|
||||
#define db_aglevel db_bmap.dn_aglevel
|
||||
#define db_agpref db_bmap.dn_agpref
|
||||
#define db_maxag db_bmap.dn_maxag
|
||||
#define db_maxfreebud db_bmap.dn_maxfreebud
|
||||
#define db_l2nbperpage db_bmap.dn_l2nbperpage
|
||||
|
||||
/*
|
||||
* macros for various conversions needed by the allocators.
|
||||
* blkstol2(), cntlz(), and cnttz() are operating system dependent functions.
|
||||
*/
|
||||
/* convert number of blocks to log2 number of blocks, rounding up to
|
||||
* the next log2 value if blocks is not a l2 multiple.
|
||||
*/
|
||||
#define BLKSTOL2(d) (blkstol2(d))
|
||||
|
||||
/* convert number of leafs to log2 leaf value */
|
||||
#define NLSTOL2BSZ(n) (31 - cntlz((n)) + BUDMIN)
|
||||
|
||||
/* convert leaf index to log2 leaf value */
|
||||
#define LITOL2BSZ(n,m,b) ((((n) == 0) ? (m) : cnttz((n))) + (b))
|
||||
|
||||
/* convert a block number to a dmap control leaf index */
|
||||
#define BLKTOCTLLEAF(b,m) \
|
||||
(((b) & (((s64)1 << ((m) + L2LPERCTL)) - 1)) >> (m))
|
||||
|
||||
/* convert log2 leaf value to buddy size */
|
||||
#define BUDSIZE(s,m) (1 << ((s) - (m)))
|
||||
|
||||
/*
|
||||
* external references.
|
||||
*/
|
||||
extern int dbMount(struct inode *ipbmap);
|
||||
|
||||
extern int dbUnmount(struct inode *ipbmap, int mounterror);
|
||||
|
||||
extern int dbFree(struct inode *ipbmap, s64 blkno, s64 nblocks);
|
||||
|
||||
extern int dbUpdatePMap(struct inode *ipbmap,
|
||||
int free, s64 blkno, s64 nblocks, struct tblock * tblk);
|
||||
|
||||
extern int dbNextAG(struct inode *ipbmap);
|
||||
|
||||
extern int dbAlloc(struct inode *ipbmap, s64 hint, s64 nblocks, s64 * results);
|
||||
|
||||
extern int dbReAlloc(struct inode *ipbmap,
|
||||
s64 blkno, s64 nblocks, s64 addnblocks, s64 * results);
|
||||
|
||||
extern int dbSync(struct inode *ipbmap);
|
||||
extern int dbAllocBottomUp(struct inode *ip, s64 blkno, s64 nblocks);
|
||||
extern int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks);
|
||||
extern void dbFinalizeBmap(struct inode *ipbmap);
|
||||
extern s64 dbMapFileSizeToMapSize(struct inode *ipbmap);
|
||||
extern s64 dbDiscardAG(struct inode *ip, int agno, s64 minlen);
|
||||
|
||||
#endif /* _H_JFS_DMAP */
|
4576
fs/jfs/jfs_dtree.c
Normal file
4576
fs/jfs/jfs_dtree.c
Normal file
File diff suppressed because it is too large
Load diff
269
fs/jfs/jfs_dtree.h
Normal file
269
fs/jfs/jfs_dtree.h
Normal file
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_DTREE
|
||||
#define _H_JFS_DTREE
|
||||
|
||||
/*
|
||||
* jfs_dtree.h: directory B+-tree manager
|
||||
*/
|
||||
|
||||
#include "jfs_btree.h"
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
tid_t tid;
|
||||
struct inode *ip;
|
||||
u32 ino;
|
||||
} leaf;
|
||||
pxd_t xd;
|
||||
} ddata_t;
|
||||
|
||||
|
||||
/*
|
||||
* entry segment/slot
|
||||
*
|
||||
* an entry consists of type dependent head/only segment/slot and
|
||||
* additional segments/slots linked vi next field;
|
||||
* N.B. last/only segment of entry is terminated by next = -1;
|
||||
*/
|
||||
/*
|
||||
* directory page slot
|
||||
*/
|
||||
struct dtslot {
|
||||
s8 next; /* 1: */
|
||||
s8 cnt; /* 1: */
|
||||
__le16 name[15]; /* 30: */
|
||||
}; /* (32) */
|
||||
|
||||
|
||||
#define DATASLOTSIZE 16
|
||||
#define L2DATASLOTSIZE 4
|
||||
#define DTSLOTSIZE 32
|
||||
#define L2DTSLOTSIZE 5
|
||||
#define DTSLOTHDRSIZE 2
|
||||
#define DTSLOTDATASIZE 30
|
||||
#define DTSLOTDATALEN 15
|
||||
|
||||
/*
|
||||
* internal node entry head/only segment
|
||||
*/
|
||||
struct idtentry {
|
||||
pxd_t xd; /* 8: child extent descriptor */
|
||||
|
||||
s8 next; /* 1: */
|
||||
u8 namlen; /* 1: */
|
||||
__le16 name[11]; /* 22: 2-byte aligned */
|
||||
}; /* (32) */
|
||||
|
||||
#define DTIHDRSIZE 10
|
||||
#define DTIHDRDATALEN 11
|
||||
|
||||
/* compute number of slots for entry */
|
||||
#define NDTINTERNAL(klen) (DIV_ROUND_UP((4 + (klen)), 15))
|
||||
|
||||
|
||||
/*
|
||||
* leaf node entry head/only segment
|
||||
*
|
||||
* For legacy filesystems, name contains 13 wchars -- no index field
|
||||
*/
|
||||
struct ldtentry {
|
||||
__le32 inumber; /* 4: 4-byte aligned */
|
||||
s8 next; /* 1: */
|
||||
u8 namlen; /* 1: */
|
||||
__le16 name[11]; /* 22: 2-byte aligned */
|
||||
__le32 index; /* 4: index into dir_table */
|
||||
}; /* (32) */
|
||||
|
||||
#define DTLHDRSIZE 6
|
||||
#define DTLHDRDATALEN_LEGACY 13 /* Old (OS/2) format */
|
||||
#define DTLHDRDATALEN 11
|
||||
|
||||
/*
|
||||
* dir_table used for directory traversal during readdir
|
||||
*/
|
||||
|
||||
/*
|
||||
* Keep persistent index for directory entries
|
||||
*/
|
||||
#define DO_INDEX(INODE) (JFS_SBI((INODE)->i_sb)->mntflag & JFS_DIR_INDEX)
|
||||
|
||||
/*
|
||||
* Maximum entry in inline directory table
|
||||
*/
|
||||
#define MAX_INLINE_DIRTABLE_ENTRY 13
|
||||
|
||||
struct dir_table_slot {
|
||||
u8 rsrvd; /* 1: */
|
||||
u8 flag; /* 1: 0 if free */
|
||||
u8 slot; /* 1: slot within leaf page of entry */
|
||||
u8 addr1; /* 1: upper 8 bits of leaf page address */
|
||||
__le32 addr2; /* 4: lower 32 bits of leaf page address -OR-
|
||||
index of next entry when this entry was deleted */
|
||||
}; /* (8) */
|
||||
|
||||
/*
|
||||
* flag values
|
||||
*/
|
||||
#define DIR_INDEX_VALID 1
|
||||
#define DIR_INDEX_FREE 0
|
||||
|
||||
#define DTSaddress(dir_table_slot, address64)\
|
||||
{\
|
||||
(dir_table_slot)->addr1 = ((u64)address64) >> 32;\
|
||||
(dir_table_slot)->addr2 = __cpu_to_le32((address64) & 0xffffffff);\
|
||||
}
|
||||
|
||||
#define addressDTS(dts)\
|
||||
( ((s64)((dts)->addr1)) << 32 | __le32_to_cpu((dts)->addr2) )
|
||||
|
||||
/* compute number of slots for entry */
|
||||
#define NDTLEAF_LEGACY(klen) (DIV_ROUND_UP((2 + (klen)), 15))
|
||||
#define NDTLEAF NDTINTERNAL
|
||||
|
||||
|
||||
/*
|
||||
* directory root page (in-line in on-disk inode):
|
||||
*
|
||||
* cf. dtpage_t below.
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
struct dasd DASD; /* 16: DASD limit/usage info */
|
||||
|
||||
u8 flag; /* 1: */
|
||||
u8 nextindex; /* 1: next free entry in stbl */
|
||||
s8 freecnt; /* 1: free count */
|
||||
s8 freelist; /* 1: freelist header */
|
||||
|
||||
__le32 idotdot; /* 4: parent inode number */
|
||||
|
||||
s8 stbl[8]; /* 8: sorted entry index table */
|
||||
} header; /* (32) */
|
||||
|
||||
struct dtslot slot[9];
|
||||
} dtroot_t;
|
||||
|
||||
#define PARENT(IP) \
|
||||
(le32_to_cpu(JFS_IP(IP)->i_dtroot.header.idotdot))
|
||||
|
||||
#define DTROOTMAXSLOT 9
|
||||
|
||||
#define dtEmpty(IP) (JFS_IP(IP)->i_dtroot.header.nextindex == 0)
|
||||
|
||||
|
||||
/*
|
||||
* directory regular page:
|
||||
*
|
||||
* entry slot array of 32 byte slot
|
||||
*
|
||||
* sorted entry slot index table (stbl):
|
||||
* contiguous slots at slot specified by stblindex,
|
||||
* 1-byte per entry
|
||||
* 512 byte block: 16 entry tbl (1 slot)
|
||||
* 1024 byte block: 32 entry tbl (1 slot)
|
||||
* 2048 byte block: 64 entry tbl (2 slot)
|
||||
* 4096 byte block: 128 entry tbl (4 slot)
|
||||
*
|
||||
* data area:
|
||||
* 512 byte block: 16 - 2 = 14 slot
|
||||
* 1024 byte block: 32 - 2 = 30 slot
|
||||
* 2048 byte block: 64 - 3 = 61 slot
|
||||
* 4096 byte block: 128 - 5 = 123 slot
|
||||
*
|
||||
* N.B. index is 0-based; index fields refer to slot index
|
||||
* except nextindex which refers to entry index in stbl;
|
||||
* end of entry stot list or freelist is marked with -1.
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
__le64 next; /* 8: next sibling */
|
||||
__le64 prev; /* 8: previous sibling */
|
||||
|
||||
u8 flag; /* 1: */
|
||||
u8 nextindex; /* 1: next entry index in stbl */
|
||||
s8 freecnt; /* 1: */
|
||||
s8 freelist; /* 1: slot index of head of freelist */
|
||||
|
||||
u8 maxslot; /* 1: number of slots in page slot[] */
|
||||
u8 stblindex; /* 1: slot index of start of stbl */
|
||||
u8 rsrvd[2]; /* 2: */
|
||||
|
||||
pxd_t self; /* 8: self pxd */
|
||||
} header; /* (32) */
|
||||
|
||||
struct dtslot slot[128];
|
||||
} dtpage_t;
|
||||
|
||||
#define DTPAGEMAXSLOT 128
|
||||
|
||||
#define DT8THPGNODEBYTES 512
|
||||
#define DT8THPGNODETSLOTS 1
|
||||
#define DT8THPGNODESLOTS 16
|
||||
|
||||
#define DTQTRPGNODEBYTES 1024
|
||||
#define DTQTRPGNODETSLOTS 1
|
||||
#define DTQTRPGNODESLOTS 32
|
||||
|
||||
#define DTHALFPGNODEBYTES 2048
|
||||
#define DTHALFPGNODETSLOTS 2
|
||||
#define DTHALFPGNODESLOTS 64
|
||||
|
||||
#define DTFULLPGNODEBYTES 4096
|
||||
#define DTFULLPGNODETSLOTS 4
|
||||
#define DTFULLPGNODESLOTS 128
|
||||
|
||||
#define DTENTRYSTART 1
|
||||
|
||||
/* get sorted entry table of the page */
|
||||
#define DT_GETSTBL(p) ( ((p)->header.flag & BT_ROOT) ?\
|
||||
((dtroot_t *)(p))->header.stbl : \
|
||||
(s8 *)&(p)->slot[(p)->header.stblindex] )
|
||||
|
||||
/*
|
||||
* Flags for dtSearch
|
||||
*/
|
||||
#define JFS_CREATE 1
|
||||
#define JFS_LOOKUP 2
|
||||
#define JFS_REMOVE 3
|
||||
#define JFS_RENAME 4
|
||||
|
||||
/*
|
||||
* Maximum file offset for directories.
|
||||
*/
|
||||
#define DIREND INT_MAX
|
||||
|
||||
/*
|
||||
* external declarations
|
||||
*/
|
||||
extern void dtInitRoot(tid_t tid, struct inode *ip, u32 idotdot);
|
||||
|
||||
extern int dtSearch(struct inode *ip, struct component_name * key,
|
||||
ino_t * data, struct btstack * btstack, int flag);
|
||||
|
||||
extern int dtInsert(tid_t tid, struct inode *ip, struct component_name * key,
|
||||
ino_t * ino, struct btstack * btstack);
|
||||
|
||||
extern int dtDelete(tid_t tid, struct inode *ip, struct component_name * key,
|
||||
ino_t * data, int flag);
|
||||
|
||||
extern int dtModify(tid_t tid, struct inode *ip, struct component_name * key,
|
||||
ino_t * orig_ino, ino_t new_ino, int flag);
|
||||
|
||||
extern int jfs_readdir(struct file *file, struct dir_context *ctx);
|
||||
#endif /* !_H_JFS_DTREE */
|
651
fs/jfs/jfs_extent.c
Normal file
651
fs/jfs/jfs_extent.c
Normal file
|
@ -0,0 +1,651 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2004
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/quotaops.h>
|
||||
#include "jfs_incore.h"
|
||||
#include "jfs_inode.h"
|
||||
#include "jfs_superblock.h"
|
||||
#include "jfs_dmap.h"
|
||||
#include "jfs_extent.h"
|
||||
#include "jfs_debug.h"
|
||||
|
||||
/*
|
||||
* forward references
|
||||
*/
|
||||
static int extBalloc(struct inode *, s64, s64 *, s64 *);
|
||||
#ifdef _NOTYET
|
||||
static int extBrealloc(struct inode *, s64, s64, s64 *, s64 *);
|
||||
#endif
|
||||
static s64 extRoundDown(s64 nb);
|
||||
|
||||
#define DPD(a) (printk("(a): %d\n",(a)))
|
||||
#define DPC(a) (printk("(a): %c\n",(a)))
|
||||
#define DPL1(a) \
|
||||
{ \
|
||||
if ((a) >> 32) \
|
||||
printk("(a): %x%08x ",(a)); \
|
||||
else \
|
||||
printk("(a): %x ",(a) << 32); \
|
||||
}
|
||||
#define DPL(a) \
|
||||
{ \
|
||||
if ((a) >> 32) \
|
||||
printk("(a): %x%08x\n",(a)); \
|
||||
else \
|
||||
printk("(a): %x\n",(a) << 32); \
|
||||
}
|
||||
|
||||
#define DPD1(a) (printk("(a): %d ",(a)))
|
||||
#define DPX(a) (printk("(a): %08x\n",(a)))
|
||||
#define DPX1(a) (printk("(a): %08x ",(a)))
|
||||
#define DPS(a) (printk("%s\n",(a)))
|
||||
#define DPE(a) (printk("\nENTERING: %s\n",(a)))
|
||||
#define DPE1(a) (printk("\nENTERING: %s",(a)))
|
||||
#define DPS1(a) (printk(" %s ",(a)))
|
||||
|
||||
|
||||
/*
|
||||
* NAME: extAlloc()
|
||||
*
|
||||
* FUNCTION: allocate an extent for a specified page range within a
|
||||
* file.
|
||||
*
|
||||
* PARAMETERS:
|
||||
* ip - the inode of the file.
|
||||
* xlen - requested extent length.
|
||||
* pno - the starting page number with the file.
|
||||
* xp - pointer to an xad. on entry, xad describes an
|
||||
* extent that is used as an allocation hint if the
|
||||
* xaddr of the xad is non-zero. on successful exit,
|
||||
* the xad describes the newly allocated extent.
|
||||
* abnr - bool indicating whether the newly allocated extent
|
||||
* should be marked as allocated but not recorded.
|
||||
*
|
||||
* RETURN VALUES:
|
||||
* 0 - success
|
||||
* -EIO - i/o error.
|
||||
* -ENOSPC - insufficient disk resources.
|
||||
*/
|
||||
int
|
||||
extAlloc(struct inode *ip, s64 xlen, s64 pno, xad_t * xp, bool abnr)
|
||||
{
|
||||
struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb);
|
||||
s64 nxlen, nxaddr, xoff, hint, xaddr = 0;
|
||||
int rc;
|
||||
int xflag;
|
||||
|
||||
/* This blocks if we are low on resources */
|
||||
txBeginAnon(ip->i_sb);
|
||||
|
||||
/* Avoid race with jfs_commit_inode() */
|
||||
mutex_lock(&JFS_IP(ip)->commit_mutex);
|
||||
|
||||
/* validate extent length */
|
||||
if (xlen > MAXXLEN)
|
||||
xlen = MAXXLEN;
|
||||
|
||||
/* get the page's starting extent offset */
|
||||
xoff = pno << sbi->l2nbperpage;
|
||||
|
||||
/* check if an allocation hint was provided */
|
||||
if ((hint = addressXAD(xp))) {
|
||||
/* get the size of the extent described by the hint */
|
||||
nxlen = lengthXAD(xp);
|
||||
|
||||
/* check if the hint is for the portion of the file
|
||||
* immediately previous to the current allocation
|
||||
* request and if hint extent has the same abnr
|
||||
* value as the current request. if so, we can
|
||||
* extend the hint extent to include the current
|
||||
* extent if we can allocate the blocks immediately
|
||||
* following the hint extent.
|
||||
*/
|
||||
if (offsetXAD(xp) + nxlen == xoff &&
|
||||
abnr == ((xp->flag & XAD_NOTRECORDED) ? true : false))
|
||||
xaddr = hint + nxlen;
|
||||
|
||||
/* adjust the hint to the last block of the extent */
|
||||
hint += (nxlen - 1);
|
||||
}
|
||||
|
||||
/* allocate the disk blocks for the extent. initially, extBalloc()
|
||||
* will try to allocate disk blocks for the requested size (xlen).
|
||||
* if this fails (xlen contiguous free blocks not available), it'll
|
||||
* try to allocate a smaller number of blocks (producing a smaller
|
||||
* extent), with this smaller number of blocks consisting of the
|
||||
* requested number of blocks rounded down to the next smaller
|
||||
* power of 2 number (i.e. 16 -> 8). it'll continue to round down
|
||||
* and retry the allocation until the number of blocks to allocate
|
||||
* is smaller than the number of blocks per page.
|
||||
*/
|
||||
nxlen = xlen;
|
||||
if ((rc = extBalloc(ip, hint ? hint : INOHINT(ip), &nxlen, &nxaddr))) {
|
||||
mutex_unlock(&JFS_IP(ip)->commit_mutex);
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/* Allocate blocks to quota. */
|
||||
rc = dquot_alloc_block(ip, nxlen);
|
||||
if (rc) {
|
||||
dbFree(ip, nxaddr, (s64) nxlen);
|
||||
mutex_unlock(&JFS_IP(ip)->commit_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* determine the value of the extent flag */
|
||||
xflag = abnr ? XAD_NOTRECORDED : 0;
|
||||
|
||||
/* if we can extend the hint extent to cover the current request,
|
||||
* extend it. otherwise, insert a new extent to
|
||||
* cover the current request.
|
||||
*/
|
||||
if (xaddr && xaddr == nxaddr)
|
||||
rc = xtExtend(0, ip, xoff, (int) nxlen, 0);
|
||||
else
|
||||
rc = xtInsert(0, ip, xflag, xoff, (int) nxlen, &nxaddr, 0);
|
||||
|
||||
/* if the extend or insert failed,
|
||||
* free the newly allocated blocks and return the error.
|
||||
*/
|
||||
if (rc) {
|
||||
dbFree(ip, nxaddr, nxlen);
|
||||
dquot_free_block(ip, nxlen);
|
||||
mutex_unlock(&JFS_IP(ip)->commit_mutex);
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/* set the results of the extent allocation */
|
||||
XADaddress(xp, nxaddr);
|
||||
XADlength(xp, nxlen);
|
||||
XADoffset(xp, xoff);
|
||||
xp->flag = xflag;
|
||||
|
||||
mark_inode_dirty(ip);
|
||||
|
||||
mutex_unlock(&JFS_IP(ip)->commit_mutex);
|
||||
/*
|
||||
* COMMIT_SyncList flags an anonymous tlock on page that is on
|
||||
* sync list.
|
||||
* We need to commit the inode to get the page written disk.
|
||||
*/
|
||||
if (test_and_clear_cflag(COMMIT_Synclist,ip))
|
||||
jfs_commit_inode(ip, 0);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _NOTYET
|
||||
/*
|
||||
* NAME: extRealloc()
|
||||
*
|
||||
* FUNCTION: extend the allocation of a file extent containing a
|
||||
* partial back last page.
|
||||
*
|
||||
* PARAMETERS:
|
||||
* ip - the inode of the file.
|
||||
* cp - cbuf for the partial backed last page.
|
||||
* xlen - request size of the resulting extent.
|
||||
* xp - pointer to an xad. on successful exit, the xad
|
||||
* describes the newly allocated extent.
|
||||
* abnr - bool indicating whether the newly allocated extent
|
||||
* should be marked as allocated but not recorded.
|
||||
*
|
||||
* RETURN VALUES:
|
||||
* 0 - success
|
||||
* -EIO - i/o error.
|
||||
* -ENOSPC - insufficient disk resources.
|
||||
*/
|
||||
int extRealloc(struct inode *ip, s64 nxlen, xad_t * xp, bool abnr)
|
||||
{
|
||||
struct super_block *sb = ip->i_sb;
|
||||
s64 xaddr, xlen, nxaddr, delta, xoff;
|
||||
s64 ntail, nextend, ninsert;
|
||||
int rc, nbperpage = JFS_SBI(sb)->nbperpage;
|
||||
int xflag;
|
||||
|
||||
/* This blocks if we are low on resources */
|
||||
txBeginAnon(ip->i_sb);
|
||||
|
||||
mutex_lock(&JFS_IP(ip)->commit_mutex);
|
||||
/* validate extent length */
|
||||
if (nxlen > MAXXLEN)
|
||||
nxlen = MAXXLEN;
|
||||
|
||||
/* get the extend (partial) page's disk block address and
|
||||
* number of blocks.
|
||||
*/
|
||||
xaddr = addressXAD(xp);
|
||||
xlen = lengthXAD(xp);
|
||||
xoff = offsetXAD(xp);
|
||||
|
||||
/* if the extend page is abnr and if the request is for
|
||||
* the extent to be allocated and recorded,
|
||||
* make the page allocated and recorded.
|
||||
*/
|
||||
if ((xp->flag & XAD_NOTRECORDED) && !abnr) {
|
||||
xp->flag = 0;
|
||||
if ((rc = xtUpdate(0, ip, xp)))
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* try to allocated the request number of blocks for the
|
||||
* extent. dbRealloc() first tries to satisfy the request
|
||||
* by extending the allocation in place. otherwise, it will
|
||||
* try to allocate a new set of blocks large enough for the
|
||||
* request. in satisfying a request, dbReAlloc() may allocate
|
||||
* less than what was request but will always allocate enough
|
||||
* space as to satisfy the extend page.
|
||||
*/
|
||||
if ((rc = extBrealloc(ip, xaddr, xlen, &nxlen, &nxaddr)))
|
||||
goto exit;
|
||||
|
||||
/* Allocat blocks to quota. */
|
||||
rc = dquot_alloc_block(ip, nxlen);
|
||||
if (rc) {
|
||||
dbFree(ip, nxaddr, (s64) nxlen);
|
||||
mutex_unlock(&JFS_IP(ip)->commit_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
delta = nxlen - xlen;
|
||||
|
||||
/* check if the extend page is not abnr but the request is abnr
|
||||
* and the allocated disk space is for more than one page. if this
|
||||
* is the case, there is a miss match of abnr between the extend page
|
||||
* and the one or more pages following the extend page. as a result,
|
||||
* two extents will have to be manipulated. the first will be that
|
||||
* of the extent of the extend page and will be manipulated thru
|
||||
* an xtExtend() or an xtTailgate(), depending upon whether the
|
||||
* disk allocation occurred as an inplace extension. the second
|
||||
* extent will be manipulated (created) through an xtInsert() and
|
||||
* will be for the pages following the extend page.
|
||||
*/
|
||||
if (abnr && (!(xp->flag & XAD_NOTRECORDED)) && (nxlen > nbperpage)) {
|
||||
ntail = nbperpage;
|
||||
nextend = ntail - xlen;
|
||||
ninsert = nxlen - nbperpage;
|
||||
|
||||
xflag = XAD_NOTRECORDED;
|
||||
} else {
|
||||
ntail = nxlen;
|
||||
nextend = delta;
|
||||
ninsert = 0;
|
||||
|
||||
xflag = xp->flag;
|
||||
}
|
||||
|
||||
/* if we were able to extend the disk allocation in place,
|
||||
* extend the extent. otherwise, move the extent to a
|
||||
* new disk location.
|
||||
*/
|
||||
if (xaddr == nxaddr) {
|
||||
/* extend the extent */
|
||||
if ((rc = xtExtend(0, ip, xoff + xlen, (int) nextend, 0))) {
|
||||
dbFree(ip, xaddr + xlen, delta);
|
||||
dquot_free_block(ip, nxlen);
|
||||
goto exit;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* move the extent to a new location:
|
||||
*
|
||||
* xtTailgate() accounts for relocated tail extent;
|
||||
*/
|
||||
if ((rc = xtTailgate(0, ip, xoff, (int) ntail, nxaddr, 0))) {
|
||||
dbFree(ip, nxaddr, nxlen);
|
||||
dquot_free_block(ip, nxlen);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* check if we need to also insert a new extent */
|
||||
if (ninsert) {
|
||||
/* perform the insert. if it fails, free the blocks
|
||||
* to be inserted and make it appear that we only did
|
||||
* the xtExtend() or xtTailgate() above.
|
||||
*/
|
||||
xaddr = nxaddr + ntail;
|
||||
if (xtInsert (0, ip, xflag, xoff + ntail, (int) ninsert,
|
||||
&xaddr, 0)) {
|
||||
dbFree(ip, xaddr, (s64) ninsert);
|
||||
delta = nextend;
|
||||
nxlen = ntail;
|
||||
xflag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* set the return results */
|
||||
XADaddress(xp, nxaddr);
|
||||
XADlength(xp, nxlen);
|
||||
XADoffset(xp, xoff);
|
||||
xp->flag = xflag;
|
||||
|
||||
mark_inode_dirty(ip);
|
||||
exit:
|
||||
mutex_unlock(&JFS_IP(ip)->commit_mutex);
|
||||
return (rc);
|
||||
}
|
||||
#endif /* _NOTYET */
|
||||
|
||||
|
||||
/*
|
||||
* NAME: extHint()
|
||||
*
|
||||
* FUNCTION: produce an extent allocation hint for a file offset.
|
||||
*
|
||||
* PARAMETERS:
|
||||
* ip - the inode of the file.
|
||||
* offset - file offset for which the hint is needed.
|
||||
* xp - pointer to the xad that is to be filled in with
|
||||
* the hint.
|
||||
*
|
||||
* RETURN VALUES:
|
||||
* 0 - success
|
||||
* -EIO - i/o error.
|
||||
*/
|
||||
int extHint(struct inode *ip, s64 offset, xad_t * xp)
|
||||
{
|
||||
struct super_block *sb = ip->i_sb;
|
||||
int nbperpage = JFS_SBI(sb)->nbperpage;
|
||||
s64 prev;
|
||||
int rc = 0;
|
||||
s64 xaddr;
|
||||
int xlen;
|
||||
int xflag;
|
||||
|
||||
/* init the hint as "no hint provided" */
|
||||
XADaddress(xp, 0);
|
||||
|
||||
/* determine the starting extent offset of the page previous
|
||||
* to the page containing the offset.
|
||||
*/
|
||||
prev = ((offset & ~POFFSET) >> JFS_SBI(sb)->l2bsize) - nbperpage;
|
||||
|
||||
/* if the offset is in the first page of the file, no hint provided.
|
||||
*/
|
||||
if (prev < 0)
|
||||
goto out;
|
||||
|
||||
rc = xtLookup(ip, prev, nbperpage, &xflag, &xaddr, &xlen, 0);
|
||||
|
||||
if ((rc == 0) && xlen) {
|
||||
if (xlen != nbperpage) {
|
||||
jfs_error(ip->i_sb, "corrupt xtree\n");
|
||||
rc = -EIO;
|
||||
}
|
||||
XADaddress(xp, xaddr);
|
||||
XADlength(xp, xlen);
|
||||
XADoffset(xp, prev);
|
||||
/*
|
||||
* only preserve the abnr flag within the xad flags
|
||||
* of the returned hint.
|
||||
*/
|
||||
xp->flag = xflag & XAD_NOTRECORDED;
|
||||
} else
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
return (rc);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* NAME: extRecord()
|
||||
*
|
||||
* FUNCTION: change a page with a file from not recorded to recorded.
|
||||
*
|
||||
* PARAMETERS:
|
||||
* ip - inode of the file.
|
||||
* cp - cbuf of the file page.
|
||||
*
|
||||
* RETURN VALUES:
|
||||
* 0 - success
|
||||
* -EIO - i/o error.
|
||||
* -ENOSPC - insufficient disk resources.
|
||||
*/
|
||||
int extRecord(struct inode *ip, xad_t * xp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
txBeginAnon(ip->i_sb);
|
||||
|
||||
mutex_lock(&JFS_IP(ip)->commit_mutex);
|
||||
|
||||
/* update the extent */
|
||||
rc = xtUpdate(0, ip, xp);
|
||||
|
||||
mutex_unlock(&JFS_IP(ip)->commit_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#ifdef _NOTYET
|
||||
/*
|
||||
* NAME: extFill()
|
||||
*
|
||||
* FUNCTION: allocate disk space for a file page that represents
|
||||
* a file hole.
|
||||
*
|
||||
* PARAMETERS:
|
||||
* ip - the inode of the file.
|
||||
* cp - cbuf of the file page represent the hole.
|
||||
*
|
||||
* RETURN VALUES:
|
||||
* 0 - success
|
||||
* -EIO - i/o error.
|
||||
* -ENOSPC - insufficient disk resources.
|
||||
*/
|
||||
int extFill(struct inode *ip, xad_t * xp)
|
||||
{
|
||||
int rc, nbperpage = JFS_SBI(ip->i_sb)->nbperpage;
|
||||
s64 blkno = offsetXAD(xp) >> ip->i_blkbits;
|
||||
|
||||
// assert(ISSPARSE(ip));
|
||||
|
||||
/* initialize the extent allocation hint */
|
||||
XADaddress(xp, 0);
|
||||
|
||||
/* allocate an extent to fill the hole */
|
||||
if ((rc = extAlloc(ip, nbperpage, blkno, xp, false)))
|
||||
return (rc);
|
||||
|
||||
assert(lengthPXD(xp) == nbperpage);
|
||||
|
||||
return (0);
|
||||
}
|
||||
#endif /* _NOTYET */
|
||||
|
||||
|
||||
/*
|
||||
* NAME: extBalloc()
|
||||
*
|
||||
* FUNCTION: allocate disk blocks to form an extent.
|
||||
*
|
||||
* initially, we will try to allocate disk blocks for the
|
||||
* requested size (nblocks). if this fails (nblocks
|
||||
* contiguous free blocks not available), we'll try to allocate
|
||||
* a smaller number of blocks (producing a smaller extent), with
|
||||
* this smaller number of blocks consisting of the requested
|
||||
* number of blocks rounded down to the next smaller power of 2
|
||||
* number (i.e. 16 -> 8). we'll continue to round down and
|
||||
* retry the allocation until the number of blocks to allocate
|
||||
* is smaller than the number of blocks per page.
|
||||
*
|
||||
* PARAMETERS:
|
||||
* ip - the inode of the file.
|
||||
* hint - disk block number to be used as an allocation hint.
|
||||
* *nblocks - pointer to an s64 value. on entry, this value specifies
|
||||
* the desired number of block to be allocated. on successful
|
||||
* exit, this value is set to the number of blocks actually
|
||||
* allocated.
|
||||
* blkno - pointer to a block address that is filled in on successful
|
||||
* return with the starting block number of the newly
|
||||
* allocated block range.
|
||||
*
|
||||
* RETURN VALUES:
|
||||
* 0 - success
|
||||
* -EIO - i/o error.
|
||||
* -ENOSPC - insufficient disk resources.
|
||||
*/
|
||||
static int
|
||||
extBalloc(struct inode *ip, s64 hint, s64 * nblocks, s64 * blkno)
|
||||
{
|
||||
struct jfs_inode_info *ji = JFS_IP(ip);
|
||||
struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb);
|
||||
s64 nb, nblks, daddr, max;
|
||||
int rc, nbperpage = sbi->nbperpage;
|
||||
struct bmap *bmp = sbi->bmap;
|
||||
int ag;
|
||||
|
||||
/* get the number of blocks to initially attempt to allocate.
|
||||
* we'll first try the number of blocks requested unless this
|
||||
* number is greater than the maximum number of contiguous free
|
||||
* blocks in the map. in that case, we'll start off with the
|
||||
* maximum free.
|
||||
*/
|
||||
max = (s64) 1 << bmp->db_maxfreebud;
|
||||
if (*nblocks >= max && *nblocks > nbperpage)
|
||||
nb = nblks = (max > nbperpage) ? max : nbperpage;
|
||||
else
|
||||
nb = nblks = *nblocks;
|
||||
|
||||
/* try to allocate blocks */
|
||||
while ((rc = dbAlloc(ip, hint, nb, &daddr)) != 0) {
|
||||
/* if something other than an out of space error,
|
||||
* stop and return this error.
|
||||
*/
|
||||
if (rc != -ENOSPC)
|
||||
return (rc);
|
||||
|
||||
/* decrease the allocation request size */
|
||||
nb = min(nblks, extRoundDown(nb));
|
||||
|
||||
/* give up if we cannot cover a page */
|
||||
if (nb < nbperpage)
|
||||
return (rc);
|
||||
}
|
||||
|
||||
*nblocks = nb;
|
||||
*blkno = daddr;
|
||||
|
||||
if (S_ISREG(ip->i_mode) && (ji->fileset == FILESYSTEM_I)) {
|
||||
ag = BLKTOAG(daddr, sbi);
|
||||
spin_lock_irq(&ji->ag_lock);
|
||||
if (ji->active_ag == -1) {
|
||||
atomic_inc(&bmp->db_active[ag]);
|
||||
ji->active_ag = ag;
|
||||
} else if (ji->active_ag != ag) {
|
||||
atomic_dec(&bmp->db_active[ji->active_ag]);
|
||||
atomic_inc(&bmp->db_active[ag]);
|
||||
ji->active_ag = ag;
|
||||
}
|
||||
spin_unlock_irq(&ji->ag_lock);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _NOTYET
|
||||
/*
|
||||
* NAME: extBrealloc()
|
||||
*
|
||||
* FUNCTION: attempt to extend an extent's allocation.
|
||||
*
|
||||
* Initially, we will try to extend the extent's allocation
|
||||
* in place. If this fails, we'll try to move the extent
|
||||
* to a new set of blocks. If moving the extent, we initially
|
||||
* will try to allocate disk blocks for the requested size
|
||||
* (newnblks). if this fails (new contiguous free blocks not
|
||||
* available), we'll try to allocate a smaller number of
|
||||
* blocks (producing a smaller extent), with this smaller
|
||||
* number of blocks consisting of the requested number of
|
||||
* blocks rounded down to the next smaller power of 2
|
||||
* number (i.e. 16 -> 8). We'll continue to round down and
|
||||
* retry the allocation until the number of blocks to allocate
|
||||
* is smaller than the number of blocks per page.
|
||||
*
|
||||
* PARAMETERS:
|
||||
* ip - the inode of the file.
|
||||
* blkno - starting block number of the extents current allocation.
|
||||
* nblks - number of blocks within the extents current allocation.
|
||||
* newnblks - pointer to a s64 value. on entry, this value is the
|
||||
* the new desired extent size (number of blocks). on
|
||||
* successful exit, this value is set to the extent's actual
|
||||
* new size (new number of blocks).
|
||||
* newblkno - the starting block number of the extents new allocation.
|
||||
*
|
||||
* RETURN VALUES:
|
||||
* 0 - success
|
||||
* -EIO - i/o error.
|
||||
* -ENOSPC - insufficient disk resources.
|
||||
*/
|
||||
static int
|
||||
extBrealloc(struct inode *ip,
|
||||
s64 blkno, s64 nblks, s64 * newnblks, s64 * newblkno)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* try to extend in place */
|
||||
if ((rc = dbExtend(ip, blkno, nblks, *newnblks - nblks)) == 0) {
|
||||
*newblkno = blkno;
|
||||
return (0);
|
||||
} else {
|
||||
if (rc != -ENOSPC)
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/* in place extension not possible.
|
||||
* try to move the extent to a new set of blocks.
|
||||
*/
|
||||
return (extBalloc(ip, blkno, newnblks, newblkno));
|
||||
}
|
||||
#endif /* _NOTYET */
|
||||
|
||||
|
||||
/*
|
||||
* NAME: extRoundDown()
|
||||
*
|
||||
* FUNCTION: round down a specified number of blocks to the next
|
||||
* smallest power of 2 number.
|
||||
*
|
||||
* PARAMETERS:
|
||||
* nb - the inode of the file.
|
||||
*
|
||||
* RETURN VALUES:
|
||||
* next smallest power of 2 number.
|
||||
*/
|
||||
static s64 extRoundDown(s64 nb)
|
||||
{
|
||||
int i;
|
||||
u64 m, k;
|
||||
|
||||
for (i = 0, m = (u64) 1 << 63; i < 64; i++, m >>= 1) {
|
||||
if (m & nb)
|
||||
break;
|
||||
}
|
||||
|
||||
i = 63 - i;
|
||||
k = (u64) 1 << i;
|
||||
k = ((k - 1) & nb) ? k : k >> 1;
|
||||
|
||||
return (k);
|
||||
}
|
31
fs/jfs/jfs_extent.h
Normal file
31
fs/jfs/jfs_extent.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2001
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_EXTENT
|
||||
#define _H_JFS_EXTENT
|
||||
|
||||
/* get block allocation allocation hint as location of disk inode */
|
||||
#define INOHINT(ip) \
|
||||
(addressPXD(&(JFS_IP(ip)->ixpxd)) + lengthPXD(&(JFS_IP(ip)->ixpxd)) - 1)
|
||||
|
||||
extern int extAlloc(struct inode *, s64, s64, xad_t *, bool);
|
||||
extern int extFill(struct inode *, xad_t *);
|
||||
extern int extHint(struct inode *, s64, xad_t *);
|
||||
extern int extRealloc(struct inode *, s64, xad_t *, bool);
|
||||
extern int extRecord(struct inode *, xad_t *);
|
||||
|
||||
#endif /* _H_JFS_EXTENT */
|
285
fs/jfs/jfs_filsys.h
Normal file
285
fs/jfs/jfs_filsys.h
Normal file
|
@ -0,0 +1,285 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2003
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_FILSYS
|
||||
#define _H_JFS_FILSYS
|
||||
|
||||
/*
|
||||
* jfs_filsys.h
|
||||
*
|
||||
* file system (implementation-dependent) constants
|
||||
*
|
||||
* refer to <limits.h> for system wide implementation-dependent constants
|
||||
*/
|
||||
|
||||
/*
|
||||
* file system option (superblock flag)
|
||||
*/
|
||||
|
||||
/* directory option */
|
||||
#define JFS_UNICODE 0x00000001 /* unicode name */
|
||||
|
||||
/* mount time flags for error handling */
|
||||
#define JFS_ERR_REMOUNT_RO 0x00000002 /* remount read-only */
|
||||
#define JFS_ERR_CONTINUE 0x00000004 /* continue */
|
||||
#define JFS_ERR_PANIC 0x00000008 /* panic */
|
||||
|
||||
/* Quota support */
|
||||
#define JFS_USRQUOTA 0x00000010
|
||||
#define JFS_GRPQUOTA 0x00000020
|
||||
|
||||
/* mount time flag to disable journaling to disk */
|
||||
#define JFS_NOINTEGRITY 0x00000040
|
||||
|
||||
/* mount time flag to enable TRIM to ssd disks */
|
||||
#define JFS_DISCARD 0x00000080
|
||||
|
||||
/* commit option */
|
||||
#define JFS_COMMIT 0x00000f00 /* commit option mask */
|
||||
#define JFS_GROUPCOMMIT 0x00000100 /* group (of 1) commit */
|
||||
#define JFS_LAZYCOMMIT 0x00000200 /* lazy commit */
|
||||
#define JFS_TMPFS 0x00000400 /* temporary file system -
|
||||
* do not log/commit:
|
||||
* Never implemented
|
||||
*/
|
||||
|
||||
/* log logical volume option */
|
||||
#define JFS_INLINELOG 0x00000800 /* inline log within file system */
|
||||
#define JFS_INLINEMOVE 0x00001000 /* inline log being moved */
|
||||
|
||||
/* Secondary aggregate inode table */
|
||||
#define JFS_BAD_SAIT 0x00010000 /* current secondary ait is bad */
|
||||
|
||||
/* sparse regular file support */
|
||||
#define JFS_SPARSE 0x00020000 /* sparse regular file */
|
||||
|
||||
/* DASD Limits F226941 */
|
||||
#define JFS_DASD_ENABLED 0x00040000 /* DASD limits enabled */
|
||||
#define JFS_DASD_PRIME 0x00080000 /* Prime DASD usage on boot */
|
||||
|
||||
/* big endian flag */
|
||||
#define JFS_SWAP_BYTES 0x00100000 /* running on big endian computer */
|
||||
|
||||
/* Directory index */
|
||||
#define JFS_DIR_INDEX 0x00200000 /* Persistent index for */
|
||||
|
||||
/* platform options */
|
||||
#define JFS_LINUX 0x10000000 /* Linux support */
|
||||
#define JFS_DFS 0x20000000 /* DCE DFS LFS support */
|
||||
/* Never implemented */
|
||||
|
||||
#define JFS_OS2 0x40000000 /* OS/2 support */
|
||||
/* case-insensitive name/directory support */
|
||||
|
||||
#define JFS_AIX 0x80000000 /* AIX support */
|
||||
|
||||
/*
|
||||
* buffer cache configuration
|
||||
*/
|
||||
/* page size */
|
||||
#ifdef PSIZE
|
||||
#undef PSIZE
|
||||
#endif
|
||||
#define PSIZE 4096 /* page size (in byte) */
|
||||
#define L2PSIZE 12 /* log2(PSIZE) */
|
||||
#define POFFSET 4095 /* offset within page */
|
||||
|
||||
/* buffer page size */
|
||||
#define BPSIZE PSIZE
|
||||
|
||||
/*
|
||||
* fs fundamental size
|
||||
*
|
||||
* PSIZE >= file system block size >= PBSIZE >= DISIZE
|
||||
*/
|
||||
#define PBSIZE 512 /* physical block size (in byte) */
|
||||
#define L2PBSIZE 9 /* log2(PBSIZE) */
|
||||
|
||||
#define DISIZE 512 /* on-disk inode size (in byte) */
|
||||
#define L2DISIZE 9 /* log2(DISIZE) */
|
||||
|
||||
#define IDATASIZE 256 /* inode inline data size */
|
||||
#define IXATTRSIZE 128 /* inode inline extended attribute size */
|
||||
|
||||
#define XTPAGE_SIZE 4096
|
||||
#define log2_PAGESIZE 12
|
||||
|
||||
#define IAG_SIZE 4096
|
||||
#define IAG_EXTENT_SIZE 4096
|
||||
#define INOSPERIAG 4096 /* number of disk inodes per iag */
|
||||
#define L2INOSPERIAG 12 /* l2 number of disk inodes per iag */
|
||||
#define INOSPEREXT 32 /* number of disk inode per extent */
|
||||
#define L2INOSPEREXT 5 /* l2 number of disk inode per extent */
|
||||
#define IXSIZE (DISIZE * INOSPEREXT) /* inode extent size */
|
||||
#define INOSPERPAGE 8 /* number of disk inodes per 4K page */
|
||||
#define L2INOSPERPAGE 3 /* log2(INOSPERPAGE) */
|
||||
|
||||
#define IAGFREELIST_LWM 64
|
||||
|
||||
#define INODE_EXTENT_SIZE IXSIZE /* inode extent size */
|
||||
#define NUM_INODE_PER_EXTENT INOSPEREXT
|
||||
#define NUM_INODE_PER_IAG INOSPERIAG
|
||||
|
||||
#define MINBLOCKSIZE 512
|
||||
#define MAXBLOCKSIZE 4096
|
||||
#define MAXFILESIZE ((s64)1 << 52)
|
||||
|
||||
#define JFS_LINK_MAX 0xffffffff
|
||||
|
||||
/* Minimum number of bytes supported for a JFS partition */
|
||||
#define MINJFS (0x1000000)
|
||||
#define MINJFSTEXT "16"
|
||||
|
||||
/*
|
||||
* file system block size -> physical block size
|
||||
*/
|
||||
#define LBOFFSET(x) ((x) & (PBSIZE - 1))
|
||||
#define LBNUMBER(x) ((x) >> L2PBSIZE)
|
||||
#define LBLK2PBLK(sb,b) ((b) << (sb->s_blocksize_bits - L2PBSIZE))
|
||||
#define PBLK2LBLK(sb,b) ((b) >> (sb->s_blocksize_bits - L2PBSIZE))
|
||||
/* size in byte -> last page number */
|
||||
#define SIZE2PN(size) ( ((s64)((size) - 1)) >> (L2PSIZE) )
|
||||
/* size in byte -> last file system block number */
|
||||
#define SIZE2BN(size, l2bsize) ( ((s64)((size) - 1)) >> (l2bsize) )
|
||||
|
||||
/*
|
||||
* fixed physical block address (physical block size = 512 byte)
|
||||
*
|
||||
* NOTE: since we can't guarantee a physical block size of 512 bytes the use of
|
||||
* these macros should be removed and the byte offset macros used instead.
|
||||
*/
|
||||
#define SUPER1_B 64 /* primary superblock */
|
||||
#define AIMAP_B (SUPER1_B + 8) /* 1st extent of aggregate inode map */
|
||||
#define AITBL_B (AIMAP_B + 16) /*
|
||||
* 1st extent of aggregate inode table
|
||||
*/
|
||||
#define SUPER2_B (AITBL_B + 32) /* 2ndary superblock pbn */
|
||||
#define BMAP_B (SUPER2_B + 8) /* block allocation map */
|
||||
|
||||
/*
|
||||
* SIZE_OF_SUPER defines the total amount of space reserved on disk for the
|
||||
* superblock. This is not the same as the superblock structure, since all of
|
||||
* this space is not currently being used.
|
||||
*/
|
||||
#define SIZE_OF_SUPER PSIZE
|
||||
|
||||
/*
|
||||
* SIZE_OF_AG_TABLE defines the amount of space reserved to hold the AG table
|
||||
*/
|
||||
#define SIZE_OF_AG_TABLE PSIZE
|
||||
|
||||
/*
|
||||
* SIZE_OF_MAP_PAGE defines the amount of disk space reserved for each page of
|
||||
* the inode allocation map (to hold iag)
|
||||
*/
|
||||
#define SIZE_OF_MAP_PAGE PSIZE
|
||||
|
||||
/*
|
||||
* fixed byte offset address
|
||||
*/
|
||||
#define SUPER1_OFF 0x8000 /* primary superblock */
|
||||
#define AIMAP_OFF (SUPER1_OFF + SIZE_OF_SUPER)
|
||||
/*
|
||||
* Control page of aggregate inode map
|
||||
* followed by 1st extent of map
|
||||
*/
|
||||
#define AITBL_OFF (AIMAP_OFF + (SIZE_OF_MAP_PAGE << 1))
|
||||
/*
|
||||
* 1st extent of aggregate inode table
|
||||
*/
|
||||
#define SUPER2_OFF (AITBL_OFF + INODE_EXTENT_SIZE)
|
||||
/*
|
||||
* secondary superblock
|
||||
*/
|
||||
#define BMAP_OFF (SUPER2_OFF + SIZE_OF_SUPER)
|
||||
/*
|
||||
* block allocation map
|
||||
*/
|
||||
|
||||
/*
|
||||
* The following macro is used to indicate the number of reserved disk blocks at
|
||||
* the front of an aggregate, in terms of physical blocks. This value is
|
||||
* currently defined to be 32K. This turns out to be the same as the primary
|
||||
* superblock's address, since it directly follows the reserved blocks.
|
||||
*/
|
||||
#define AGGR_RSVD_BLOCKS SUPER1_B
|
||||
|
||||
/*
|
||||
* The following macro is used to indicate the number of reserved bytes at the
|
||||
* front of an aggregate. This value is currently defined to be 32K. This
|
||||
* turns out to be the same as the primary superblock's byte offset, since it
|
||||
* directly follows the reserved blocks.
|
||||
*/
|
||||
#define AGGR_RSVD_BYTES SUPER1_OFF
|
||||
|
||||
/*
|
||||
* The following macro defines the byte offset for the first inode extent in
|
||||
* the aggregate inode table. This allows us to find the self inode to find the
|
||||
* rest of the table. Currently this value is 44K.
|
||||
*/
|
||||
#define AGGR_INODE_TABLE_START AITBL_OFF
|
||||
|
||||
/*
|
||||
* fixed reserved inode number
|
||||
*/
|
||||
/* aggregate inode */
|
||||
#define AGGR_RESERVED_I 0 /* aggregate inode (reserved) */
|
||||
#define AGGREGATE_I 1 /* aggregate inode map inode */
|
||||
#define BMAP_I 2 /* aggregate block allocation map inode */
|
||||
#define LOG_I 3 /* aggregate inline log inode */
|
||||
#define BADBLOCK_I 4 /* aggregate bad block inode */
|
||||
#define FILESYSTEM_I 16 /* 1st/only fileset inode in ait:
|
||||
* fileset inode map inode
|
||||
*/
|
||||
|
||||
/* per fileset inode */
|
||||
#define FILESET_RSVD_I 0 /* fileset inode (reserved) */
|
||||
#define FILESET_EXT_I 1 /* fileset inode extension */
|
||||
#define ROOT_I 2 /* fileset root inode */
|
||||
#define ACL_I 3 /* fileset ACL inode */
|
||||
|
||||
#define FILESET_OBJECT_I 4 /* the first fileset inode available for a file
|
||||
* or directory or link...
|
||||
*/
|
||||
#define FIRST_FILESET_INO 16 /* the first aggregate inode which describes
|
||||
* an inode. (To fsck this is also the first
|
||||
* inode in part 2 of the agg inode table.)
|
||||
*/
|
||||
|
||||
/*
|
||||
* directory configuration
|
||||
*/
|
||||
#define JFS_NAME_MAX 255
|
||||
#define JFS_PATH_MAX BPSIZE
|
||||
|
||||
|
||||
/*
|
||||
* file system state (superblock state)
|
||||
*/
|
||||
#define FM_CLEAN 0x00000000 /* file system is unmounted and clean */
|
||||
#define FM_MOUNT 0x00000001 /* file system is mounted cleanly */
|
||||
#define FM_DIRTY 0x00000002 /* file system was not unmounted and clean
|
||||
* when mounted or
|
||||
* commit failure occurred while being mounted:
|
||||
* fsck() must be run to repair
|
||||
*/
|
||||
#define FM_LOGREDO 0x00000004 /* log based recovery (logredo()) failed:
|
||||
* fsck() must be run to repair
|
||||
*/
|
||||
#define FM_EXTENDFS 0x00000008 /* file system extendfs() in progress */
|
||||
|
||||
#endif /* _H_JFS_FILSYS */
|
3178
fs/jfs/jfs_imap.c
Normal file
3178
fs/jfs/jfs_imap.c
Normal file
File diff suppressed because it is too large
Load diff
175
fs/jfs/jfs_imap.h
Normal file
175
fs/jfs/jfs_imap.h
Normal file
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_IMAP
|
||||
#define _H_JFS_IMAP
|
||||
|
||||
#include "jfs_txnmgr.h"
|
||||
|
||||
/*
|
||||
* jfs_imap.h: disk inode manager
|
||||
*/
|
||||
|
||||
#define EXTSPERIAG 128 /* number of disk inode extent per iag */
|
||||
#define IMAPBLKNO 0 /* lblkno of dinomap within inode map */
|
||||
#define SMAPSZ 4 /* number of words per summary map */
|
||||
#define EXTSPERSUM 32 /* number of extents per summary map entry */
|
||||
#define L2EXTSPERSUM 5 /* l2 number of extents per summary map */
|
||||
#define PGSPERIEXT 4 /* number of 4K pages per dinode extent */
|
||||
#define MAXIAGS ((1<<20)-1) /* maximum number of iags */
|
||||
#define MAXAG 128 /* maximum number of allocation groups */
|
||||
|
||||
#define AMAPSIZE 512 /* bytes in the IAG allocation maps */
|
||||
#define SMAPSIZE 16 /* bytes in the IAG summary maps */
|
||||
|
||||
/* convert inode number to iag number */
|
||||
#define INOTOIAG(ino) ((ino) >> L2INOSPERIAG)
|
||||
|
||||
/* convert iag number to logical block number of the iag page */
|
||||
#define IAGTOLBLK(iagno,l2nbperpg) (((iagno) + 1) << (l2nbperpg))
|
||||
|
||||
/* get the starting block number of the 4K page of an inode extent
|
||||
* that contains ino.
|
||||
*/
|
||||
#define INOPBLK(pxd,ino,l2nbperpg) (addressPXD((pxd)) + \
|
||||
((((ino) & (INOSPEREXT-1)) >> L2INOSPERPAGE) << (l2nbperpg)))
|
||||
|
||||
/*
|
||||
* inode allocation map:
|
||||
*
|
||||
* inode allocation map consists of
|
||||
* . the inode map control page and
|
||||
* . inode allocation group pages (per 4096 inodes)
|
||||
* which are addressed by standard JFS xtree.
|
||||
*/
|
||||
/*
|
||||
* inode allocation group page (per 4096 inodes of an AG)
|
||||
*/
|
||||
struct iag {
|
||||
__le64 agstart; /* 8: starting block of ag */
|
||||
__le32 iagnum; /* 4: inode allocation group number */
|
||||
__le32 inofreefwd; /* 4: ag inode free list forward */
|
||||
__le32 inofreeback; /* 4: ag inode free list back */
|
||||
__le32 extfreefwd; /* 4: ag inode extent free list forward */
|
||||
__le32 extfreeback; /* 4: ag inode extent free list back */
|
||||
__le32 iagfree; /* 4: iag free list */
|
||||
|
||||
/* summary map: 1 bit per inode extent */
|
||||
__le32 inosmap[SMAPSZ]; /* 16: sum map of mapwords w/ free inodes;
|
||||
* note: this indicates free and backed
|
||||
* inodes, if the extent is not backed the
|
||||
* value will be 1. if the extent is
|
||||
* backed but all inodes are being used the
|
||||
* value will be 1. if the extent is
|
||||
* backed but at least one of the inodes is
|
||||
* free the value will be 0.
|
||||
*/
|
||||
__le32 extsmap[SMAPSZ]; /* 16: sum map of mapwords w/ free extents */
|
||||
__le32 nfreeinos; /* 4: number of free inodes */
|
||||
__le32 nfreeexts; /* 4: number of free extents */
|
||||
/* (72) */
|
||||
u8 pad[1976]; /* 1976: pad to 2048 bytes */
|
||||
/* allocation bit map: 1 bit per inode (0 - free, 1 - allocated) */
|
||||
__le32 wmap[EXTSPERIAG]; /* 512: working allocation map */
|
||||
__le32 pmap[EXTSPERIAG]; /* 512: persistent allocation map */
|
||||
pxd_t inoext[EXTSPERIAG]; /* 1024: inode extent addresses */
|
||||
}; /* (4096) */
|
||||
|
||||
/*
|
||||
* per AG control information (in inode map control page)
|
||||
*/
|
||||
struct iagctl_disk {
|
||||
__le32 inofree; /* 4: free inode list anchor */
|
||||
__le32 extfree; /* 4: free extent list anchor */
|
||||
__le32 numinos; /* 4: number of backed inodes */
|
||||
__le32 numfree; /* 4: number of free inodes */
|
||||
}; /* (16) */
|
||||
|
||||
struct iagctl {
|
||||
int inofree; /* free inode list anchor */
|
||||
int extfree; /* free extent list anchor */
|
||||
int numinos; /* number of backed inodes */
|
||||
int numfree; /* number of free inodes */
|
||||
};
|
||||
|
||||
/*
|
||||
* per fileset/aggregate inode map control page
|
||||
*/
|
||||
struct dinomap_disk {
|
||||
__le32 in_freeiag; /* 4: free iag list anchor */
|
||||
__le32 in_nextiag; /* 4: next free iag number */
|
||||
__le32 in_numinos; /* 4: num of backed inodes */
|
||||
__le32 in_numfree; /* 4: num of free backed inodes */
|
||||
__le32 in_nbperiext; /* 4: num of blocks per inode extent */
|
||||
__le32 in_l2nbperiext; /* 4: l2 of in_nbperiext */
|
||||
__le32 in_diskblock; /* 4: for standalone test driver */
|
||||
__le32 in_maxag; /* 4: for standalone test driver */
|
||||
u8 pad[2016]; /* 2016: pad to 2048 */
|
||||
struct iagctl_disk in_agctl[MAXAG]; /* 2048: AG control information */
|
||||
}; /* (4096) */
|
||||
|
||||
struct dinomap {
|
||||
int in_freeiag; /* free iag list anchor */
|
||||
int in_nextiag; /* next free iag number */
|
||||
int in_numinos; /* num of backed inodes */
|
||||
int in_numfree; /* num of free backed inodes */
|
||||
int in_nbperiext; /* num of blocks per inode extent */
|
||||
int in_l2nbperiext; /* l2 of in_nbperiext */
|
||||
int in_diskblock; /* for standalone test driver */
|
||||
int in_maxag; /* for standalone test driver */
|
||||
struct iagctl in_agctl[MAXAG]; /* AG control information */
|
||||
};
|
||||
|
||||
/*
|
||||
* In-core inode map control page
|
||||
*/
|
||||
struct inomap {
|
||||
struct dinomap im_imap; /* 4096: inode allocation control */
|
||||
struct inode *im_ipimap; /* 4: ptr to inode for imap */
|
||||
struct mutex im_freelock; /* 4: iag free list lock */
|
||||
struct mutex im_aglock[MAXAG]; /* 512: per AG locks */
|
||||
u32 *im_DBGdimap;
|
||||
atomic_t im_numinos; /* num of backed inodes */
|
||||
atomic_t im_numfree; /* num of free backed inodes */
|
||||
};
|
||||
|
||||
#define im_freeiag im_imap.in_freeiag
|
||||
#define im_nextiag im_imap.in_nextiag
|
||||
#define im_agctl im_imap.in_agctl
|
||||
#define im_nbperiext im_imap.in_nbperiext
|
||||
#define im_l2nbperiext im_imap.in_l2nbperiext
|
||||
|
||||
/* for standalone testdriver
|
||||
*/
|
||||
#define im_diskblock im_imap.in_diskblock
|
||||
#define im_maxag im_imap.in_maxag
|
||||
|
||||
extern int diFree(struct inode *);
|
||||
extern int diAlloc(struct inode *, bool, struct inode *);
|
||||
extern int diSync(struct inode *);
|
||||
/* external references */
|
||||
extern int diUpdatePMap(struct inode *ipimap, unsigned long inum,
|
||||
bool is_free, struct tblock * tblk);
|
||||
extern int diExtendFS(struct inode *ipimap, struct inode *ipbmap);
|
||||
extern int diMount(struct inode *);
|
||||
extern int diUnmount(struct inode *, int);
|
||||
extern int diRead(struct inode *);
|
||||
extern struct inode *diReadSpecial(struct super_block *, ino_t, int);
|
||||
extern void diWriteSpecial(struct inode *, int);
|
||||
extern void diFreeSpecial(struct inode *);
|
||||
extern int diWrite(tid_t tid, struct inode *);
|
||||
#endif /* _H_JFS_IMAP */
|
225
fs/jfs/jfs_incore.h
Normal file
225
fs/jfs/jfs_incore.h
Normal file
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2004
|
||||
* Portions Copyright (C) Christoph Hellwig, 2001-2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_INCORE
|
||||
#define _H_JFS_INCORE
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitops.h>
|
||||
#include "jfs_types.h"
|
||||
#include "jfs_xtree.h"
|
||||
#include "jfs_dtree.h"
|
||||
|
||||
/*
|
||||
* JFS magic number
|
||||
*/
|
||||
#define JFS_SUPER_MAGIC 0x3153464a /* "JFS1" */
|
||||
|
||||
/*
|
||||
* JFS-private inode information
|
||||
*/
|
||||
struct jfs_inode_info {
|
||||
int fileset; /* fileset number (always 16)*/
|
||||
uint mode2; /* jfs-specific mode */
|
||||
kuid_t saved_uid; /* saved for uid mount option */
|
||||
kgid_t saved_gid; /* saved for gid mount option */
|
||||
pxd_t ixpxd; /* inode extent descriptor */
|
||||
dxd_t acl; /* dxd describing acl */
|
||||
dxd_t ea; /* dxd describing ea */
|
||||
time_t otime; /* time created */
|
||||
uint next_index; /* next available directory entry index */
|
||||
int acltype; /* Type of ACL */
|
||||
short btorder; /* access order */
|
||||
short btindex; /* btpage entry index*/
|
||||
struct inode *ipimap; /* inode map */
|
||||
unsigned long cflag; /* commit flags */
|
||||
u64 agstart; /* agstart of the containing IAG */
|
||||
u16 bxflag; /* xflag of pseudo buffer? */
|
||||
unchar pad;
|
||||
signed char active_ag; /* ag currently allocating from */
|
||||
lid_t blid; /* lid of pseudo buffer? */
|
||||
lid_t atlhead; /* anonymous tlock list head */
|
||||
lid_t atltail; /* anonymous tlock list tail */
|
||||
spinlock_t ag_lock; /* protects active_ag */
|
||||
struct list_head anon_inode_list; /* inodes having anonymous txns */
|
||||
/*
|
||||
* rdwrlock serializes xtree between reads & writes and synchronizes
|
||||
* changes to special inodes. It's use would be redundant on
|
||||
* directories since the i_mutex taken in the VFS is sufficient.
|
||||
*/
|
||||
struct rw_semaphore rdwrlock;
|
||||
/*
|
||||
* commit_mutex serializes transaction processing on an inode.
|
||||
* It must be taken after beginning a transaction (txBegin), since
|
||||
* dirty inodes may be committed while a new transaction on the
|
||||
* inode is blocked in txBegin or TxBeginAnon
|
||||
*/
|
||||
struct mutex commit_mutex;
|
||||
/* xattr_sem allows us to access the xattrs without taking i_mutex */
|
||||
struct rw_semaphore xattr_sem;
|
||||
lid_t xtlid; /* lid of xtree lock on directory */
|
||||
union {
|
||||
struct {
|
||||
xtpage_t _xtroot; /* 288: xtree root */
|
||||
struct inomap *_imap; /* 4: inode map header */
|
||||
} file;
|
||||
struct {
|
||||
struct dir_table_slot _table[12]; /* 96: dir index */
|
||||
dtroot_t _dtroot; /* 288: dtree root */
|
||||
} dir;
|
||||
struct {
|
||||
unchar _unused[16]; /* 16: */
|
||||
dxd_t _dxd; /* 16: */
|
||||
unchar _inline[128]; /* 128: inline symlink */
|
||||
/* _inline_ea may overlay the last part of
|
||||
* file._xtroot if maxentry = XTROOTINITSLOT
|
||||
*/
|
||||
unchar _inline_ea[128]; /* 128: inline extended attr */
|
||||
} link;
|
||||
} u;
|
||||
u32 dev; /* will die when we get wide dev_t */
|
||||
struct inode vfs_inode;
|
||||
};
|
||||
#define i_xtroot u.file._xtroot
|
||||
#define i_imap u.file._imap
|
||||
#define i_dirtable u.dir._table
|
||||
#define i_dtroot u.dir._dtroot
|
||||
#define i_inline u.link._inline
|
||||
#define i_inline_ea u.link._inline_ea
|
||||
|
||||
#define IREAD_LOCK(ip, subclass) \
|
||||
down_read_nested(&JFS_IP(ip)->rdwrlock, subclass)
|
||||
#define IREAD_UNLOCK(ip) up_read(&JFS_IP(ip)->rdwrlock)
|
||||
#define IWRITE_LOCK(ip, subclass) \
|
||||
down_write_nested(&JFS_IP(ip)->rdwrlock, subclass)
|
||||
#define IWRITE_UNLOCK(ip) up_write(&JFS_IP(ip)->rdwrlock)
|
||||
|
||||
/*
|
||||
* cflag
|
||||
*/
|
||||
enum cflags {
|
||||
COMMIT_Nolink, /* inode committed with zero link count */
|
||||
COMMIT_Inlineea, /* commit inode inline EA */
|
||||
COMMIT_Freewmap, /* free WMAP at iClose() */
|
||||
COMMIT_Dirty, /* Inode is really dirty */
|
||||
COMMIT_Dirtable, /* commit changes to di_dirtable */
|
||||
COMMIT_Stale, /* data extent is no longer valid */
|
||||
COMMIT_Synclist, /* metadata pages on group commit synclist */
|
||||
};
|
||||
|
||||
/*
|
||||
* commit_mutex nesting subclasses:
|
||||
*/
|
||||
enum commit_mutex_class
|
||||
{
|
||||
COMMIT_MUTEX_PARENT,
|
||||
COMMIT_MUTEX_CHILD,
|
||||
COMMIT_MUTEX_SECOND_PARENT, /* Renaming */
|
||||
COMMIT_MUTEX_VICTIM /* Inode being unlinked due to rename */
|
||||
};
|
||||
|
||||
/*
|
||||
* rdwrlock subclasses:
|
||||
* The dmap inode may be locked while a normal inode or the imap inode are
|
||||
* locked.
|
||||
*/
|
||||
enum rdwrlock_class
|
||||
{
|
||||
RDWRLOCK_NORMAL,
|
||||
RDWRLOCK_IMAP,
|
||||
RDWRLOCK_DMAP
|
||||
};
|
||||
|
||||
#define set_cflag(flag, ip) set_bit(flag, &(JFS_IP(ip)->cflag))
|
||||
#define clear_cflag(flag, ip) clear_bit(flag, &(JFS_IP(ip)->cflag))
|
||||
#define test_cflag(flag, ip) test_bit(flag, &(JFS_IP(ip)->cflag))
|
||||
#define test_and_clear_cflag(flag, ip) \
|
||||
test_and_clear_bit(flag, &(JFS_IP(ip)->cflag))
|
||||
/*
|
||||
* JFS-private superblock information.
|
||||
*/
|
||||
struct jfs_sb_info {
|
||||
struct super_block *sb; /* Point back to vfs super block */
|
||||
unsigned long mntflag; /* aggregate attributes */
|
||||
struct inode *ipbmap; /* block map inode */
|
||||
struct inode *ipaimap; /* aggregate inode map inode */
|
||||
struct inode *ipaimap2; /* secondary aimap inode */
|
||||
struct inode *ipimap; /* aggregate inode map inode */
|
||||
struct jfs_log *log; /* log */
|
||||
struct list_head log_list; /* volumes associated with a journal */
|
||||
short bsize; /* logical block size */
|
||||
short l2bsize; /* log2 logical block size */
|
||||
short nbperpage; /* blocks per page */
|
||||
short l2nbperpage; /* log2 blocks per page */
|
||||
short l2niperblk; /* log2 inodes per page */
|
||||
dev_t logdev; /* external log device */
|
||||
uint aggregate; /* volume identifier in log record */
|
||||
pxd_t logpxd; /* pxd describing log */
|
||||
pxd_t fsckpxd; /* pxd describing fsck wkspc */
|
||||
pxd_t ait2; /* pxd describing AIT copy */
|
||||
char uuid[16]; /* 128-bit uuid for volume */
|
||||
char loguuid[16]; /* 128-bit uuid for log */
|
||||
/*
|
||||
* commit_state is used for synchronization of the jfs_commit
|
||||
* threads. It is protected by LAZY_LOCK().
|
||||
*/
|
||||
int commit_state; /* commit state */
|
||||
/* Formerly in ipimap */
|
||||
uint gengen; /* inode generation generator*/
|
||||
uint inostamp; /* shows inode belongs to fileset*/
|
||||
|
||||
/* Formerly in ipbmap */
|
||||
struct bmap *bmap; /* incore bmap descriptor */
|
||||
struct nls_table *nls_tab; /* current codepage */
|
||||
struct inode *direct_inode; /* metadata inode */
|
||||
uint state; /* mount/recovery state */
|
||||
unsigned long flag; /* mount time flags */
|
||||
uint p_state; /* state prior to going no integrity */
|
||||
kuid_t uid; /* uid to override on-disk uid */
|
||||
kgid_t gid; /* gid to override on-disk gid */
|
||||
uint umask; /* umask to override on-disk umask */
|
||||
uint minblks_trim; /* minimum blocks, for online trim */
|
||||
};
|
||||
|
||||
/* jfs_sb_info commit_state */
|
||||
#define IN_LAZYCOMMIT 1
|
||||
|
||||
static inline struct jfs_inode_info *JFS_IP(struct inode *inode)
|
||||
{
|
||||
return list_entry(inode, struct jfs_inode_info, vfs_inode);
|
||||
}
|
||||
|
||||
static inline int jfs_dirtable_inline(struct inode *inode)
|
||||
{
|
||||
return (JFS_IP(inode)->next_index <= (MAX_INLINE_DIRTABLE_ENTRY + 1));
|
||||
}
|
||||
|
||||
static inline struct jfs_sb_info *JFS_SBI(struct super_block *sb)
|
||||
{
|
||||
return sb->s_fs_info;
|
||||
}
|
||||
|
||||
static inline int isReadOnly(struct inode *inode)
|
||||
{
|
||||
if (JFS_SBI(inode->i_sb)->log)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
#endif /* _H_JFS_INCORE */
|
165
fs/jfs/jfs_inode.c
Normal file
165
fs/jfs/jfs_inode.c
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2004
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/quotaops.h>
|
||||
#include "jfs_incore.h"
|
||||
#include "jfs_inode.h"
|
||||
#include "jfs_filsys.h"
|
||||
#include "jfs_imap.h"
|
||||
#include "jfs_dinode.h"
|
||||
#include "jfs_debug.h"
|
||||
|
||||
|
||||
void jfs_set_inode_flags(struct inode *inode)
|
||||
{
|
||||
unsigned int flags = JFS_IP(inode)->mode2;
|
||||
unsigned int new_fl = 0;
|
||||
|
||||
if (flags & JFS_IMMUTABLE_FL)
|
||||
new_fl |= S_IMMUTABLE;
|
||||
if (flags & JFS_APPEND_FL)
|
||||
new_fl |= S_APPEND;
|
||||
if (flags & JFS_NOATIME_FL)
|
||||
new_fl |= S_NOATIME;
|
||||
if (flags & JFS_DIRSYNC_FL)
|
||||
new_fl |= S_DIRSYNC;
|
||||
if (flags & JFS_SYNC_FL)
|
||||
new_fl |= S_SYNC;
|
||||
inode_set_flags(inode, new_fl, S_IMMUTABLE | S_APPEND | S_NOATIME |
|
||||
S_DIRSYNC | S_SYNC);
|
||||
}
|
||||
|
||||
void jfs_get_inode_flags(struct jfs_inode_info *jfs_ip)
|
||||
{
|
||||
unsigned int flags = jfs_ip->vfs_inode.i_flags;
|
||||
|
||||
jfs_ip->mode2 &= ~(JFS_IMMUTABLE_FL | JFS_APPEND_FL | JFS_NOATIME_FL |
|
||||
JFS_DIRSYNC_FL | JFS_SYNC_FL);
|
||||
if (flags & S_IMMUTABLE)
|
||||
jfs_ip->mode2 |= JFS_IMMUTABLE_FL;
|
||||
if (flags & S_APPEND)
|
||||
jfs_ip->mode2 |= JFS_APPEND_FL;
|
||||
if (flags & S_NOATIME)
|
||||
jfs_ip->mode2 |= JFS_NOATIME_FL;
|
||||
if (flags & S_DIRSYNC)
|
||||
jfs_ip->mode2 |= JFS_DIRSYNC_FL;
|
||||
if (flags & S_SYNC)
|
||||
jfs_ip->mode2 |= JFS_SYNC_FL;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: ialloc()
|
||||
*
|
||||
* FUNCTION: Allocate a new inode
|
||||
*
|
||||
*/
|
||||
struct inode *ialloc(struct inode *parent, umode_t mode)
|
||||
{
|
||||
struct super_block *sb = parent->i_sb;
|
||||
struct inode *inode;
|
||||
struct jfs_inode_info *jfs_inode;
|
||||
int rc;
|
||||
|
||||
inode = new_inode(sb);
|
||||
if (!inode) {
|
||||
jfs_warn("ialloc: new_inode returned NULL!");
|
||||
rc = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
jfs_inode = JFS_IP(inode);
|
||||
|
||||
rc = diAlloc(parent, S_ISDIR(mode), inode);
|
||||
if (rc) {
|
||||
jfs_warn("ialloc: diAlloc returned %d!", rc);
|
||||
if (rc == -EIO)
|
||||
make_bad_inode(inode);
|
||||
goto fail_put;
|
||||
}
|
||||
|
||||
if (insert_inode_locked(inode) < 0) {
|
||||
rc = -EINVAL;
|
||||
goto fail_put;
|
||||
}
|
||||
|
||||
inode_init_owner(inode, parent, mode);
|
||||
/*
|
||||
* New inodes need to save sane values on disk when
|
||||
* uid & gid mount options are used
|
||||
*/
|
||||
jfs_inode->saved_uid = inode->i_uid;
|
||||
jfs_inode->saved_gid = inode->i_gid;
|
||||
|
||||
/*
|
||||
* Allocate inode to quota.
|
||||
*/
|
||||
dquot_initialize(inode);
|
||||
rc = dquot_alloc_inode(inode);
|
||||
if (rc)
|
||||
goto fail_drop;
|
||||
|
||||
/* inherit flags from parent */
|
||||
jfs_inode->mode2 = JFS_IP(parent)->mode2 & JFS_FL_INHERIT;
|
||||
|
||||
if (S_ISDIR(mode)) {
|
||||
jfs_inode->mode2 |= IDIRECTORY;
|
||||
jfs_inode->mode2 &= ~JFS_DIRSYNC_FL;
|
||||
}
|
||||
else {
|
||||
jfs_inode->mode2 |= INLINEEA | ISPARSE;
|
||||
if (S_ISLNK(mode))
|
||||
jfs_inode->mode2 &= ~(JFS_IMMUTABLE_FL|JFS_APPEND_FL);
|
||||
}
|
||||
jfs_inode->mode2 |= inode->i_mode;
|
||||
|
||||
inode->i_blocks = 0;
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
|
||||
jfs_inode->otime = inode->i_ctime.tv_sec;
|
||||
inode->i_generation = JFS_SBI(sb)->gengen++;
|
||||
|
||||
jfs_inode->cflag = 0;
|
||||
|
||||
/* Zero remaining fields */
|
||||
memset(&jfs_inode->acl, 0, sizeof(dxd_t));
|
||||
memset(&jfs_inode->ea, 0, sizeof(dxd_t));
|
||||
jfs_inode->next_index = 0;
|
||||
jfs_inode->acltype = 0;
|
||||
jfs_inode->btorder = 0;
|
||||
jfs_inode->btindex = 0;
|
||||
jfs_inode->bxflag = 0;
|
||||
jfs_inode->blid = 0;
|
||||
jfs_inode->atlhead = 0;
|
||||
jfs_inode->atltail = 0;
|
||||
jfs_inode->xtlid = 0;
|
||||
jfs_set_inode_flags(inode);
|
||||
|
||||
jfs_info("ialloc returns inode = 0x%p\n", inode);
|
||||
|
||||
return inode;
|
||||
|
||||
fail_drop:
|
||||
dquot_drop(inode);
|
||||
inode->i_flags |= S_NOQUOTA;
|
||||
clear_nlink(inode);
|
||||
unlock_new_inode(inode);
|
||||
fail_put:
|
||||
iput(inode);
|
||||
fail:
|
||||
return ERR_PTR(rc);
|
||||
}
|
53
fs/jfs/jfs_inode.h
Normal file
53
fs/jfs/jfs_inode.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2001
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_INODE
|
||||
#define _H_JFS_INODE
|
||||
|
||||
struct fid;
|
||||
|
||||
extern struct inode *ialloc(struct inode *, umode_t);
|
||||
extern int jfs_fsync(struct file *, loff_t, loff_t, int);
|
||||
extern long jfs_ioctl(struct file *, unsigned int, unsigned long);
|
||||
extern long jfs_compat_ioctl(struct file *, unsigned int, unsigned long);
|
||||
extern struct inode *jfs_iget(struct super_block *, unsigned long);
|
||||
extern int jfs_commit_inode(struct inode *, int);
|
||||
extern int jfs_write_inode(struct inode *, struct writeback_control *);
|
||||
extern void jfs_evict_inode(struct inode *);
|
||||
extern void jfs_dirty_inode(struct inode *, int);
|
||||
extern void jfs_truncate(struct inode *);
|
||||
extern void jfs_truncate_nolock(struct inode *, loff_t);
|
||||
extern void jfs_free_zero_link(struct inode *);
|
||||
extern struct dentry *jfs_get_parent(struct dentry *dentry);
|
||||
extern void jfs_get_inode_flags(struct jfs_inode_info *);
|
||||
extern struct dentry *jfs_fh_to_dentry(struct super_block *sb, struct fid *fid,
|
||||
int fh_len, int fh_type);
|
||||
extern struct dentry *jfs_fh_to_parent(struct super_block *sb, struct fid *fid,
|
||||
int fh_len, int fh_type);
|
||||
extern void jfs_set_inode_flags(struct inode *);
|
||||
extern int jfs_get_block(struct inode *, sector_t, struct buffer_head *, int);
|
||||
extern int jfs_setattr(struct dentry *, struct iattr *);
|
||||
|
||||
extern const struct address_space_operations jfs_aops;
|
||||
extern const struct inode_operations jfs_dir_inode_operations;
|
||||
extern const struct file_operations jfs_dir_operations;
|
||||
extern const struct inode_operations jfs_file_inode_operations;
|
||||
extern const struct file_operations jfs_file_operations;
|
||||
extern const struct inode_operations jfs_symlink_inode_operations;
|
||||
extern const struct inode_operations jfs_fast_symlink_inode_operations;
|
||||
extern const struct dentry_operations jfs_ci_dentry_operations;
|
||||
#endif /* _H_JFS_INODE */
|
52
fs/jfs/jfs_lock.h
Normal file
52
fs/jfs/jfs_lock.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2001
|
||||
* Portions Copyright (C) Christoph Hellwig, 2001-2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_LOCK
|
||||
#define _H_JFS_LOCK
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
/*
|
||||
* jfs_lock.h
|
||||
*/
|
||||
|
||||
/*
|
||||
* Conditional sleep where condition is protected by spinlock
|
||||
*
|
||||
* lock_cmd and unlock_cmd take and release the spinlock
|
||||
*/
|
||||
#define __SLEEP_COND(wq, cond, lock_cmd, unlock_cmd) \
|
||||
do { \
|
||||
DECLARE_WAITQUEUE(__wait, current); \
|
||||
\
|
||||
add_wait_queue(&wq, &__wait); \
|
||||
for (;;) { \
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);\
|
||||
if (cond) \
|
||||
break; \
|
||||
unlock_cmd; \
|
||||
io_schedule(); \
|
||||
lock_cmd; \
|
||||
} \
|
||||
__set_current_state(TASK_RUNNING); \
|
||||
remove_wait_queue(&wq, &__wait); \
|
||||
} while (0)
|
||||
|
||||
#endif /* _H_JFS_LOCK */
|
2533
fs/jfs/jfs_logmgr.c
Normal file
2533
fs/jfs/jfs_logmgr.c
Normal file
File diff suppressed because it is too large
Load diff
513
fs/jfs/jfs_logmgr.h
Normal file
513
fs/jfs/jfs_logmgr.h
Normal file
|
@ -0,0 +1,513 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2004
|
||||
* Portions Copyright (C) Christoph Hellwig, 2001-2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_LOGMGR
|
||||
#define _H_JFS_LOGMGR
|
||||
|
||||
#include "jfs_filsys.h"
|
||||
#include "jfs_lock.h"
|
||||
|
||||
/*
|
||||
* log manager configuration parameters
|
||||
*/
|
||||
|
||||
/* log page size */
|
||||
#define LOGPSIZE 4096
|
||||
#define L2LOGPSIZE 12
|
||||
|
||||
#define LOGPAGES 16 /* Log pages per mounted file system */
|
||||
|
||||
/*
|
||||
* log logical volume
|
||||
*
|
||||
* a log is used to make the commit operation on journalled
|
||||
* files within the same logical volume group atomic.
|
||||
* a log is implemented with a logical volume.
|
||||
* there is one log per logical volume group.
|
||||
*
|
||||
* block 0 of the log logical volume is not used (ipl etc).
|
||||
* block 1 contains a log "superblock" and is used by logFormat(),
|
||||
* lmLogInit(), lmLogShutdown(), and logRedo() to record status
|
||||
* of the log but is not otherwise used during normal processing.
|
||||
* blocks 2 - (N-1) are used to contain log records.
|
||||
*
|
||||
* when a volume group is varied-on-line, logRedo() must have
|
||||
* been executed before the file systems (logical volumes) in
|
||||
* the volume group can be mounted.
|
||||
*/
|
||||
/*
|
||||
* log superblock (block 1 of logical volume)
|
||||
*/
|
||||
#define LOGSUPER_B 1
|
||||
#define LOGSTART_B 2
|
||||
|
||||
#define LOGMAGIC 0x87654321
|
||||
#define LOGVERSION 1
|
||||
|
||||
#define MAX_ACTIVE 128 /* Max active file systems sharing log */
|
||||
|
||||
struct logsuper {
|
||||
__le32 magic; /* 4: log lv identifier */
|
||||
__le32 version; /* 4: version number */
|
||||
__le32 serial; /* 4: log open/mount counter */
|
||||
__le32 size; /* 4: size in number of LOGPSIZE blocks */
|
||||
__le32 bsize; /* 4: logical block size in byte */
|
||||
__le32 l2bsize; /* 4: log2 of bsize */
|
||||
|
||||
__le32 flag; /* 4: option */
|
||||
__le32 state; /* 4: state - see below */
|
||||
|
||||
__le32 end; /* 4: addr of last log record set by logredo */
|
||||
char uuid[16]; /* 16: 128-bit journal uuid */
|
||||
char label[16]; /* 16: journal label */
|
||||
struct {
|
||||
char uuid[16];
|
||||
} active[MAX_ACTIVE]; /* 2048: active file systems list */
|
||||
};
|
||||
|
||||
#define NULL_UUID "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
|
||||
/* log flag: commit option (see jfs_filsys.h) */
|
||||
|
||||
/* log state */
|
||||
#define LOGMOUNT 0 /* log mounted by lmLogInit() */
|
||||
#define LOGREDONE 1 /* log shutdown by lmLogShutdown().
|
||||
* log redo completed by logredo().
|
||||
*/
|
||||
#define LOGWRAP 2 /* log wrapped */
|
||||
#define LOGREADERR 3 /* log read error detected in logredo() */
|
||||
|
||||
|
||||
/*
|
||||
* log logical page
|
||||
*
|
||||
* (this comment should be rewritten !)
|
||||
* the header and trailer structures (h,t) will normally have
|
||||
* the same page and eor value.
|
||||
* An exception to this occurs when a complete page write is not
|
||||
* accomplished on a power failure. Since the hardware may "split write"
|
||||
* sectors in the page, any out of order sequence may occur during powerfail
|
||||
* and needs to be recognized during log replay. The xor value is
|
||||
* an "exclusive or" of all log words in the page up to eor. This
|
||||
* 32 bit eor is stored with the top 16 bits in the header and the
|
||||
* bottom 16 bits in the trailer. logredo can easily recognize pages
|
||||
* that were not completed by reconstructing this eor and checking
|
||||
* the log page.
|
||||
*
|
||||
* Previous versions of the operating system did not allow split
|
||||
* writes and detected partially written records in logredo by
|
||||
* ordering the updates to the header, trailer, and the move of data
|
||||
* into the logdata area. The order: (1) data is moved (2) header
|
||||
* is updated (3) trailer is updated. In logredo, when the header
|
||||
* differed from the trailer, the header and trailer were reconciled
|
||||
* as follows: if h.page != t.page they were set to the smaller of
|
||||
* the two and h.eor and t.eor set to 8 (i.e. empty page). if (only)
|
||||
* h.eor != t.eor they were set to the smaller of their two values.
|
||||
*/
|
||||
struct logpage {
|
||||
struct { /* header */
|
||||
__le32 page; /* 4: log sequence page number */
|
||||
__le16 rsrvd; /* 2: */
|
||||
__le16 eor; /* 2: end-of-log offset of lasrt record write */
|
||||
} h;
|
||||
|
||||
__le32 data[LOGPSIZE / 4 - 4]; /* log record area */
|
||||
|
||||
struct { /* trailer */
|
||||
__le32 page; /* 4: normally the same as h.page */
|
||||
__le16 rsrvd; /* 2: */
|
||||
__le16 eor; /* 2: normally the same as h.eor */
|
||||
} t;
|
||||
};
|
||||
|
||||
#define LOGPHDRSIZE 8 /* log page header size */
|
||||
#define LOGPTLRSIZE 8 /* log page trailer size */
|
||||
|
||||
|
||||
/*
|
||||
* log record
|
||||
*
|
||||
* (this comment should be rewritten !)
|
||||
* jfs uses only "after" log records (only a single writer is allowed
|
||||
* in a page, pages are written to temporary paging space if
|
||||
* if they must be written to disk before commit, and i/o is
|
||||
* scheduled for modified pages to their home location after
|
||||
* the log records containing the after values and the commit
|
||||
* record is written to the log on disk, undo discards the copy
|
||||
* in main-memory.)
|
||||
*
|
||||
* a log record consists of a data area of variable length followed by
|
||||
* a descriptor of fixed size LOGRDSIZE bytes.
|
||||
* the data area is rounded up to an integral number of 4-bytes and
|
||||
* must be no longer than LOGPSIZE.
|
||||
* the descriptor is of size of multiple of 4-bytes and aligned on a
|
||||
* 4-byte boundary.
|
||||
* records are packed one after the other in the data area of log pages.
|
||||
* (sometimes a DUMMY record is inserted so that at least one record ends
|
||||
* on every page or the longest record is placed on at most two pages).
|
||||
* the field eor in page header/trailer points to the byte following
|
||||
* the last record on a page.
|
||||
*/
|
||||
|
||||
/* log record types */
|
||||
#define LOG_COMMIT 0x8000
|
||||
#define LOG_SYNCPT 0x4000
|
||||
#define LOG_MOUNT 0x2000
|
||||
#define LOG_REDOPAGE 0x0800
|
||||
#define LOG_NOREDOPAGE 0x0080
|
||||
#define LOG_NOREDOINOEXT 0x0040
|
||||
#define LOG_UPDATEMAP 0x0008
|
||||
#define LOG_NOREDOFILE 0x0001
|
||||
|
||||
/* REDOPAGE/NOREDOPAGE log record data type */
|
||||
#define LOG_INODE 0x0001
|
||||
#define LOG_XTREE 0x0002
|
||||
#define LOG_DTREE 0x0004
|
||||
#define LOG_BTROOT 0x0010
|
||||
#define LOG_EA 0x0020
|
||||
#define LOG_ACL 0x0040
|
||||
#define LOG_DATA 0x0080
|
||||
#define LOG_NEW 0x0100
|
||||
#define LOG_EXTEND 0x0200
|
||||
#define LOG_RELOCATE 0x0400
|
||||
#define LOG_DIR_XTREE 0x0800 /* Xtree is in directory inode */
|
||||
|
||||
/* UPDATEMAP log record descriptor type */
|
||||
#define LOG_ALLOCXADLIST 0x0080
|
||||
#define LOG_ALLOCPXDLIST 0x0040
|
||||
#define LOG_ALLOCXAD 0x0020
|
||||
#define LOG_ALLOCPXD 0x0010
|
||||
#define LOG_FREEXADLIST 0x0008
|
||||
#define LOG_FREEPXDLIST 0x0004
|
||||
#define LOG_FREEXAD 0x0002
|
||||
#define LOG_FREEPXD 0x0001
|
||||
|
||||
|
||||
struct lrd {
|
||||
/*
|
||||
* type independent area
|
||||
*/
|
||||
__le32 logtid; /* 4: log transaction identifier */
|
||||
__le32 backchain; /* 4: ptr to prev record of same transaction */
|
||||
__le16 type; /* 2: record type */
|
||||
__le16 length; /* 2: length of data in record (in byte) */
|
||||
__le32 aggregate; /* 4: file system lv/aggregate */
|
||||
/* (16) */
|
||||
|
||||
/*
|
||||
* type dependent area (20)
|
||||
*/
|
||||
union {
|
||||
|
||||
/*
|
||||
* COMMIT: commit
|
||||
*
|
||||
* transaction commit: no type-dependent information;
|
||||
*/
|
||||
|
||||
/*
|
||||
* REDOPAGE: after-image
|
||||
*
|
||||
* apply after-image;
|
||||
*
|
||||
* N.B. REDOPAGE, NOREDOPAGE, and UPDATEMAP must be same format;
|
||||
*/
|
||||
struct {
|
||||
__le32 fileset; /* 4: fileset number */
|
||||
__le32 inode; /* 4: inode number */
|
||||
__le16 type; /* 2: REDOPAGE record type */
|
||||
__le16 l2linesize; /* 2: log2 of line size */
|
||||
pxd_t pxd; /* 8: on-disk page pxd */
|
||||
} redopage; /* (20) */
|
||||
|
||||
/*
|
||||
* NOREDOPAGE: the page is freed
|
||||
*
|
||||
* do not apply after-image records which precede this record
|
||||
* in the log with the same page block number to this page.
|
||||
*
|
||||
* N.B. REDOPAGE, NOREDOPAGE, and UPDATEMAP must be same format;
|
||||
*/
|
||||
struct {
|
||||
__le32 fileset; /* 4: fileset number */
|
||||
__le32 inode; /* 4: inode number */
|
||||
__le16 type; /* 2: NOREDOPAGE record type */
|
||||
__le16 rsrvd; /* 2: reserved */
|
||||
pxd_t pxd; /* 8: on-disk page pxd */
|
||||
} noredopage; /* (20) */
|
||||
|
||||
/*
|
||||
* UPDATEMAP: update block allocation map
|
||||
*
|
||||
* either in-line PXD,
|
||||
* or out-of-line XADLIST;
|
||||
*
|
||||
* N.B. REDOPAGE, NOREDOPAGE, and UPDATEMAP must be same format;
|
||||
*/
|
||||
struct {
|
||||
__le32 fileset; /* 4: fileset number */
|
||||
__le32 inode; /* 4: inode number */
|
||||
__le16 type; /* 2: UPDATEMAP record type */
|
||||
__le16 nxd; /* 2: number of extents */
|
||||
pxd_t pxd; /* 8: pxd */
|
||||
} updatemap; /* (20) */
|
||||
|
||||
/*
|
||||
* NOREDOINOEXT: the inode extent is freed
|
||||
*
|
||||
* do not apply after-image records which precede this
|
||||
* record in the log with the any of the 4 page block
|
||||
* numbers in this inode extent.
|
||||
*
|
||||
* NOTE: The fileset and pxd fields MUST remain in
|
||||
* the same fields in the REDOPAGE record format.
|
||||
*
|
||||
*/
|
||||
struct {
|
||||
__le32 fileset; /* 4: fileset number */
|
||||
__le32 iagnum; /* 4: IAG number */
|
||||
__le32 inoext_idx; /* 4: inode extent index */
|
||||
pxd_t pxd; /* 8: on-disk page pxd */
|
||||
} noredoinoext; /* (20) */
|
||||
|
||||
/*
|
||||
* SYNCPT: log sync point
|
||||
*
|
||||
* replay log up to syncpt address specified;
|
||||
*/
|
||||
struct {
|
||||
__le32 sync; /* 4: syncpt address (0 = here) */
|
||||
} syncpt;
|
||||
|
||||
/*
|
||||
* MOUNT: file system mount
|
||||
*
|
||||
* file system mount: no type-dependent information;
|
||||
*/
|
||||
|
||||
/*
|
||||
* ? FREEXTENT: free specified extent(s)
|
||||
*
|
||||
* free specified extent(s) from block allocation map
|
||||
* N.B.: nextents should be length of data/sizeof(xad_t)
|
||||
*/
|
||||
struct {
|
||||
__le32 type; /* 4: FREEXTENT record type */
|
||||
__le32 nextent; /* 4: number of extents */
|
||||
|
||||
/* data: PXD or XAD list */
|
||||
} freextent;
|
||||
|
||||
/*
|
||||
* ? NOREDOFILE: this file is freed
|
||||
*
|
||||
* do not apply records which precede this record in the log
|
||||
* with the same inode number.
|
||||
*
|
||||
* NOREDOFILE must be the first to be written at commit
|
||||
* (last to be read in logredo()) - it prevents
|
||||
* replay of preceding updates of all preceding generations
|
||||
* of the inumber esp. the on-disk inode itself.
|
||||
*/
|
||||
struct {
|
||||
__le32 fileset; /* 4: fileset number */
|
||||
__le32 inode; /* 4: inode number */
|
||||
} noredofile;
|
||||
|
||||
/*
|
||||
* ? NEWPAGE:
|
||||
*
|
||||
* metadata type dependent
|
||||
*/
|
||||
struct {
|
||||
__le32 fileset; /* 4: fileset number */
|
||||
__le32 inode; /* 4: inode number */
|
||||
__le32 type; /* 4: NEWPAGE record type */
|
||||
pxd_t pxd; /* 8: on-disk page pxd */
|
||||
} newpage;
|
||||
|
||||
/*
|
||||
* ? DUMMY: filler
|
||||
*
|
||||
* no type-dependent information
|
||||
*/
|
||||
} log;
|
||||
}; /* (36) */
|
||||
|
||||
#define LOGRDSIZE (sizeof(struct lrd))
|
||||
|
||||
/*
|
||||
* line vector descriptor
|
||||
*/
|
||||
struct lvd {
|
||||
__le16 offset;
|
||||
__le16 length;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* log logical volume
|
||||
*/
|
||||
struct jfs_log {
|
||||
|
||||
struct list_head sb_list;/* This is used to sync metadata
|
||||
* before writing syncpt.
|
||||
*/
|
||||
struct list_head journal_list; /* Global list */
|
||||
struct block_device *bdev; /* 4: log lv pointer */
|
||||
int serial; /* 4: log mount serial number */
|
||||
|
||||
s64 base; /* @8: log extent address (inline log ) */
|
||||
int size; /* 4: log size in log page (in page) */
|
||||
int l2bsize; /* 4: log2 of bsize */
|
||||
|
||||
unsigned long flag; /* 4: flag */
|
||||
|
||||
struct lbuf *lbuf_free; /* 4: free lbufs */
|
||||
wait_queue_head_t free_wait; /* 4: */
|
||||
|
||||
/* log write */
|
||||
int logtid; /* 4: log tid */
|
||||
int page; /* 4: page number of eol page */
|
||||
int eor; /* 4: eor of last record in eol page */
|
||||
struct lbuf *bp; /* 4: current log page buffer */
|
||||
|
||||
struct mutex loglock; /* 4: log write serialization lock */
|
||||
|
||||
/* syncpt */
|
||||
int nextsync; /* 4: bytes to write before next syncpt */
|
||||
int active; /* 4: */
|
||||
wait_queue_head_t syncwait; /* 4: */
|
||||
|
||||
/* commit */
|
||||
uint cflag; /* 4: */
|
||||
struct list_head cqueue; /* FIFO commit queue */
|
||||
struct tblock *flush_tblk; /* tblk we're waiting on for flush */
|
||||
int gcrtc; /* 4: GC_READY transaction count */
|
||||
struct tblock *gclrt; /* 4: latest GC_READY transaction */
|
||||
spinlock_t gclock; /* 4: group commit lock */
|
||||
int logsize; /* 4: log data area size in byte */
|
||||
int lsn; /* 4: end-of-log */
|
||||
int clsn; /* 4: clsn */
|
||||
int syncpt; /* 4: addr of last syncpt record */
|
||||
int sync; /* 4: addr from last logsync() */
|
||||
struct list_head synclist; /* 8: logsynclist anchor */
|
||||
spinlock_t synclock; /* 4: synclist lock */
|
||||
struct lbuf *wqueue; /* 4: log pageout queue */
|
||||
int count; /* 4: count */
|
||||
char uuid[16]; /* 16: 128-bit uuid of log device */
|
||||
|
||||
int no_integrity; /* 3: flag to disable journaling to disk */
|
||||
};
|
||||
|
||||
/*
|
||||
* Log flag
|
||||
*/
|
||||
#define log_INLINELOG 1
|
||||
#define log_SYNCBARRIER 2
|
||||
#define log_QUIESCE 3
|
||||
#define log_FLUSH 4
|
||||
|
||||
/*
|
||||
* group commit flag
|
||||
*/
|
||||
/* jfs_log */
|
||||
#define logGC_PAGEOUT 0x00000001
|
||||
|
||||
/* tblock/lbuf */
|
||||
#define tblkGC_QUEUE 0x0001
|
||||
#define tblkGC_READY 0x0002
|
||||
#define tblkGC_COMMIT 0x0004
|
||||
#define tblkGC_COMMITTED 0x0008
|
||||
#define tblkGC_EOP 0x0010
|
||||
#define tblkGC_FREE 0x0020
|
||||
#define tblkGC_LEADER 0x0040
|
||||
#define tblkGC_ERROR 0x0080
|
||||
#define tblkGC_LAZY 0x0100 // D230860
|
||||
#define tblkGC_UNLOCKED 0x0200 // D230860
|
||||
|
||||
/*
|
||||
* log cache buffer header
|
||||
*/
|
||||
struct lbuf {
|
||||
struct jfs_log *l_log; /* 4: log associated with buffer */
|
||||
|
||||
/*
|
||||
* data buffer base area
|
||||
*/
|
||||
uint l_flag; /* 4: pageout control flags */
|
||||
|
||||
struct lbuf *l_wqnext; /* 4: write queue link */
|
||||
struct lbuf *l_freelist; /* 4: freelistlink */
|
||||
|
||||
int l_pn; /* 4: log page number */
|
||||
int l_eor; /* 4: log record eor */
|
||||
int l_ceor; /* 4: committed log record eor */
|
||||
|
||||
s64 l_blkno; /* 8: log page block number */
|
||||
caddr_t l_ldata; /* 4: data page */
|
||||
struct page *l_page; /* The page itself */
|
||||
uint l_offset; /* Offset of l_ldata within the page */
|
||||
|
||||
wait_queue_head_t l_ioevent; /* 4: i/o done event */
|
||||
};
|
||||
|
||||
/* Reuse l_freelist for redrive list */
|
||||
#define l_redrive_next l_freelist
|
||||
|
||||
/*
|
||||
* logsynclist block
|
||||
*
|
||||
* common logsyncblk prefix for jbuf_t and tblock
|
||||
*/
|
||||
struct logsyncblk {
|
||||
u16 xflag; /* flags */
|
||||
u16 flag; /* only meaninful in tblock */
|
||||
lid_t lid; /* lock id */
|
||||
s32 lsn; /* log sequence number */
|
||||
struct list_head synclist; /* log sync list link */
|
||||
};
|
||||
|
||||
/*
|
||||
* logsynclist serialization (per log)
|
||||
*/
|
||||
|
||||
#define LOGSYNC_LOCK_INIT(log) spin_lock_init(&(log)->synclock)
|
||||
#define LOGSYNC_LOCK(log, flags) spin_lock_irqsave(&(log)->synclock, flags)
|
||||
#define LOGSYNC_UNLOCK(log, flags) \
|
||||
spin_unlock_irqrestore(&(log)->synclock, flags)
|
||||
|
||||
/* compute the difference in bytes of lsn from sync point */
|
||||
#define logdiff(diff, lsn, log)\
|
||||
{\
|
||||
diff = (lsn) - (log)->syncpt;\
|
||||
if (diff < 0)\
|
||||
diff += (log)->logsize;\
|
||||
}
|
||||
|
||||
extern int lmLogOpen(struct super_block *sb);
|
||||
extern int lmLogClose(struct super_block *sb);
|
||||
extern int lmLogShutdown(struct jfs_log * log);
|
||||
extern int lmLogInit(struct jfs_log * log);
|
||||
extern int lmLogFormat(struct jfs_log *log, s64 logAddress, int logSize);
|
||||
extern int lmGroupCommit(struct jfs_log *, struct tblock *);
|
||||
extern int jfsIOWait(void *);
|
||||
extern void jfs_flush_journal(struct jfs_log * log, int wait);
|
||||
extern void jfs_syncpt(struct jfs_log *log, int hard_sync);
|
||||
|
||||
#endif /* _H_JFS_LOGMGR */
|
844
fs/jfs/jfs_metapage.c
Normal file
844
fs/jfs/jfs_metapage.c
Normal file
|
@ -0,0 +1,844 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2005
|
||||
* Portions Copyright (C) Christoph Hellwig, 2001-2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/mempool.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include "jfs_incore.h"
|
||||
#include "jfs_superblock.h"
|
||||
#include "jfs_filsys.h"
|
||||
#include "jfs_metapage.h"
|
||||
#include "jfs_txnmgr.h"
|
||||
#include "jfs_debug.h"
|
||||
|
||||
#ifdef CONFIG_JFS_STATISTICS
|
||||
static struct {
|
||||
uint pagealloc; /* # of page allocations */
|
||||
uint pagefree; /* # of page frees */
|
||||
uint lockwait; /* # of sleeping lock_metapage() calls */
|
||||
} mpStat;
|
||||
#endif
|
||||
|
||||
#define metapage_locked(mp) test_bit(META_locked, &(mp)->flag)
|
||||
#define trylock_metapage(mp) test_and_set_bit_lock(META_locked, &(mp)->flag)
|
||||
|
||||
static inline void unlock_metapage(struct metapage *mp)
|
||||
{
|
||||
clear_bit_unlock(META_locked, &mp->flag);
|
||||
wake_up(&mp->wait);
|
||||
}
|
||||
|
||||
static inline void __lock_metapage(struct metapage *mp)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
INCREMENT(mpStat.lockwait);
|
||||
add_wait_queue_exclusive(&mp->wait, &wait);
|
||||
do {
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
if (metapage_locked(mp)) {
|
||||
unlock_page(mp->page);
|
||||
io_schedule();
|
||||
lock_page(mp->page);
|
||||
}
|
||||
} while (trylock_metapage(mp));
|
||||
__set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&mp->wait, &wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* Must have mp->page locked
|
||||
*/
|
||||
static inline void lock_metapage(struct metapage *mp)
|
||||
{
|
||||
if (trylock_metapage(mp))
|
||||
__lock_metapage(mp);
|
||||
}
|
||||
|
||||
#define METAPOOL_MIN_PAGES 32
|
||||
static struct kmem_cache *metapage_cache;
|
||||
static mempool_t *metapage_mempool;
|
||||
|
||||
#define MPS_PER_PAGE (PAGE_CACHE_SIZE >> L2PSIZE)
|
||||
|
||||
#if MPS_PER_PAGE > 1
|
||||
|
||||
struct meta_anchor {
|
||||
int mp_count;
|
||||
atomic_t io_count;
|
||||
struct metapage *mp[MPS_PER_PAGE];
|
||||
};
|
||||
#define mp_anchor(page) ((struct meta_anchor *)page_private(page))
|
||||
|
||||
static inline struct metapage *page_to_mp(struct page *page, int offset)
|
||||
{
|
||||
if (!PagePrivate(page))
|
||||
return NULL;
|
||||
return mp_anchor(page)->mp[offset >> L2PSIZE];
|
||||
}
|
||||
|
||||
static inline int insert_metapage(struct page *page, struct metapage *mp)
|
||||
{
|
||||
struct meta_anchor *a;
|
||||
int index;
|
||||
int l2mp_blocks; /* log2 blocks per metapage */
|
||||
|
||||
if (PagePrivate(page))
|
||||
a = mp_anchor(page);
|
||||
else {
|
||||
a = kzalloc(sizeof(struct meta_anchor), GFP_NOFS);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
set_page_private(page, (unsigned long)a);
|
||||
SetPagePrivate(page);
|
||||
kmap(page);
|
||||
}
|
||||
|
||||
if (mp) {
|
||||
l2mp_blocks = L2PSIZE - page->mapping->host->i_blkbits;
|
||||
index = (mp->index >> l2mp_blocks) & (MPS_PER_PAGE - 1);
|
||||
a->mp_count++;
|
||||
a->mp[index] = mp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void remove_metapage(struct page *page, struct metapage *mp)
|
||||
{
|
||||
struct meta_anchor *a = mp_anchor(page);
|
||||
int l2mp_blocks = L2PSIZE - page->mapping->host->i_blkbits;
|
||||
int index;
|
||||
|
||||
index = (mp->index >> l2mp_blocks) & (MPS_PER_PAGE - 1);
|
||||
|
||||
BUG_ON(a->mp[index] != mp);
|
||||
|
||||
a->mp[index] = NULL;
|
||||
if (--a->mp_count == 0) {
|
||||
kfree(a);
|
||||
set_page_private(page, 0);
|
||||
ClearPagePrivate(page);
|
||||
kunmap(page);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void inc_io(struct page *page)
|
||||
{
|
||||
atomic_inc(&mp_anchor(page)->io_count);
|
||||
}
|
||||
|
||||
static inline void dec_io(struct page *page, void (*handler) (struct page *))
|
||||
{
|
||||
if (atomic_dec_and_test(&mp_anchor(page)->io_count))
|
||||
handler(page);
|
||||
}
|
||||
|
||||
#else
|
||||
static inline struct metapage *page_to_mp(struct page *page, int offset)
|
||||
{
|
||||
return PagePrivate(page) ? (struct metapage *)page_private(page) : NULL;
|
||||
}
|
||||
|
||||
static inline int insert_metapage(struct page *page, struct metapage *mp)
|
||||
{
|
||||
if (mp) {
|
||||
set_page_private(page, (unsigned long)mp);
|
||||
SetPagePrivate(page);
|
||||
kmap(page);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void remove_metapage(struct page *page, struct metapage *mp)
|
||||
{
|
||||
set_page_private(page, 0);
|
||||
ClearPagePrivate(page);
|
||||
kunmap(page);
|
||||
}
|
||||
|
||||
#define inc_io(page) do {} while(0)
|
||||
#define dec_io(page, handler) handler(page)
|
||||
|
||||
#endif
|
||||
|
||||
static void init_once(void *foo)
|
||||
{
|
||||
struct metapage *mp = (struct metapage *)foo;
|
||||
|
||||
mp->lid = 0;
|
||||
mp->lsn = 0;
|
||||
mp->flag = 0;
|
||||
mp->data = NULL;
|
||||
mp->clsn = 0;
|
||||
mp->log = NULL;
|
||||
set_bit(META_free, &mp->flag);
|
||||
init_waitqueue_head(&mp->wait);
|
||||
}
|
||||
|
||||
static inline struct metapage *alloc_metapage(gfp_t gfp_mask)
|
||||
{
|
||||
return mempool_alloc(metapage_mempool, gfp_mask);
|
||||
}
|
||||
|
||||
static inline void free_metapage(struct metapage *mp)
|
||||
{
|
||||
mp->flag = 0;
|
||||
set_bit(META_free, &mp->flag);
|
||||
|
||||
mempool_free(mp, metapage_mempool);
|
||||
}
|
||||
|
||||
int __init metapage_init(void)
|
||||
{
|
||||
/*
|
||||
* Allocate the metapage structures
|
||||
*/
|
||||
metapage_cache = kmem_cache_create("jfs_mp", sizeof(struct metapage),
|
||||
0, 0, init_once);
|
||||
if (metapage_cache == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
metapage_mempool = mempool_create_slab_pool(METAPOOL_MIN_PAGES,
|
||||
metapage_cache);
|
||||
|
||||
if (metapage_mempool == NULL) {
|
||||
kmem_cache_destroy(metapage_cache);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void metapage_exit(void)
|
||||
{
|
||||
mempool_destroy(metapage_mempool);
|
||||
kmem_cache_destroy(metapage_cache);
|
||||
}
|
||||
|
||||
static inline void drop_metapage(struct page *page, struct metapage *mp)
|
||||
{
|
||||
if (mp->count || mp->nohomeok || test_bit(META_dirty, &mp->flag) ||
|
||||
test_bit(META_io, &mp->flag))
|
||||
return;
|
||||
remove_metapage(page, mp);
|
||||
INCREMENT(mpStat.pagefree);
|
||||
free_metapage(mp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Metapage address space operations
|
||||
*/
|
||||
|
||||
static sector_t metapage_get_blocks(struct inode *inode, sector_t lblock,
|
||||
int *len)
|
||||
{
|
||||
int rc = 0;
|
||||
int xflag;
|
||||
s64 xaddr;
|
||||
sector_t file_blocks = (inode->i_size + inode->i_sb->s_blocksize - 1) >>
|
||||
inode->i_blkbits;
|
||||
|
||||
if (lblock >= file_blocks)
|
||||
return 0;
|
||||
if (lblock + *len > file_blocks)
|
||||
*len = file_blocks - lblock;
|
||||
|
||||
if (inode->i_ino) {
|
||||
rc = xtLookup(inode, (s64)lblock, *len, &xflag, &xaddr, len, 0);
|
||||
if ((rc == 0) && *len)
|
||||
lblock = (sector_t)xaddr;
|
||||
else
|
||||
lblock = 0;
|
||||
} /* else no mapping */
|
||||
|
||||
return lblock;
|
||||
}
|
||||
|
||||
static void last_read_complete(struct page *page)
|
||||
{
|
||||
if (!PageError(page))
|
||||
SetPageUptodate(page);
|
||||
unlock_page(page);
|
||||
}
|
||||
|
||||
static void metapage_read_end_io(struct bio *bio, int err)
|
||||
{
|
||||
struct page *page = bio->bi_private;
|
||||
|
||||
if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) {
|
||||
printk(KERN_ERR "metapage_read_end_io: I/O error\n");
|
||||
SetPageError(page);
|
||||
}
|
||||
|
||||
dec_io(page, last_read_complete);
|
||||
bio_put(bio);
|
||||
}
|
||||
|
||||
static void remove_from_logsync(struct metapage *mp)
|
||||
{
|
||||
struct jfs_log *log = mp->log;
|
||||
unsigned long flags;
|
||||
/*
|
||||
* This can race. Recheck that log hasn't been set to null, and after
|
||||
* acquiring logsync lock, recheck lsn
|
||||
*/
|
||||
if (!log)
|
||||
return;
|
||||
|
||||
LOGSYNC_LOCK(log, flags);
|
||||
if (mp->lsn) {
|
||||
mp->log = NULL;
|
||||
mp->lsn = 0;
|
||||
mp->clsn = 0;
|
||||
log->count--;
|
||||
list_del(&mp->synclist);
|
||||
}
|
||||
LOGSYNC_UNLOCK(log, flags);
|
||||
}
|
||||
|
||||
static void last_write_complete(struct page *page)
|
||||
{
|
||||
struct metapage *mp;
|
||||
unsigned int offset;
|
||||
|
||||
for (offset = 0; offset < PAGE_CACHE_SIZE; offset += PSIZE) {
|
||||
mp = page_to_mp(page, offset);
|
||||
if (mp && test_bit(META_io, &mp->flag)) {
|
||||
if (mp->lsn)
|
||||
remove_from_logsync(mp);
|
||||
clear_bit(META_io, &mp->flag);
|
||||
}
|
||||
/*
|
||||
* I'd like to call drop_metapage here, but I don't think it's
|
||||
* safe unless I have the page locked
|
||||
*/
|
||||
}
|
||||
end_page_writeback(page);
|
||||
}
|
||||
|
||||
static void metapage_write_end_io(struct bio *bio, int err)
|
||||
{
|
||||
struct page *page = bio->bi_private;
|
||||
|
||||
BUG_ON(!PagePrivate(page));
|
||||
|
||||
if (! test_bit(BIO_UPTODATE, &bio->bi_flags)) {
|
||||
printk(KERN_ERR "metapage_write_end_io: I/O error\n");
|
||||
SetPageError(page);
|
||||
}
|
||||
dec_io(page, last_write_complete);
|
||||
bio_put(bio);
|
||||
}
|
||||
|
||||
static int metapage_writepage(struct page *page, struct writeback_control *wbc)
|
||||
{
|
||||
struct bio *bio = NULL;
|
||||
int block_offset; /* block offset of mp within page */
|
||||
struct inode *inode = page->mapping->host;
|
||||
int blocks_per_mp = JFS_SBI(inode->i_sb)->nbperpage;
|
||||
int len;
|
||||
int xlen;
|
||||
struct metapage *mp;
|
||||
int redirty = 0;
|
||||
sector_t lblock;
|
||||
int nr_underway = 0;
|
||||
sector_t pblock;
|
||||
sector_t next_block = 0;
|
||||
sector_t page_start;
|
||||
unsigned long bio_bytes = 0;
|
||||
unsigned long bio_offset = 0;
|
||||
int offset;
|
||||
int bad_blocks = 0;
|
||||
|
||||
page_start = (sector_t)page->index <<
|
||||
(PAGE_CACHE_SHIFT - inode->i_blkbits);
|
||||
BUG_ON(!PageLocked(page));
|
||||
BUG_ON(PageWriteback(page));
|
||||
set_page_writeback(page);
|
||||
|
||||
for (offset = 0; offset < PAGE_CACHE_SIZE; offset += PSIZE) {
|
||||
mp = page_to_mp(page, offset);
|
||||
|
||||
if (!mp || !test_bit(META_dirty, &mp->flag))
|
||||
continue;
|
||||
|
||||
if (mp->nohomeok && !test_bit(META_forcewrite, &mp->flag)) {
|
||||
redirty = 1;
|
||||
/*
|
||||
* Make sure this page isn't blocked indefinitely.
|
||||
* If the journal isn't undergoing I/O, push it
|
||||
*/
|
||||
if (mp->log && !(mp->log->cflag & logGC_PAGEOUT))
|
||||
jfs_flush_journal(mp->log, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
clear_bit(META_dirty, &mp->flag);
|
||||
set_bit(META_io, &mp->flag);
|
||||
block_offset = offset >> inode->i_blkbits;
|
||||
lblock = page_start + block_offset;
|
||||
if (bio) {
|
||||
if (xlen && lblock == next_block) {
|
||||
/* Contiguous, in memory & on disk */
|
||||
len = min(xlen, blocks_per_mp);
|
||||
xlen -= len;
|
||||
bio_bytes += len << inode->i_blkbits;
|
||||
continue;
|
||||
}
|
||||
/* Not contiguous */
|
||||
if (bio_add_page(bio, page, bio_bytes, bio_offset) <
|
||||
bio_bytes)
|
||||
goto add_failed;
|
||||
/*
|
||||
* Increment counter before submitting i/o to keep
|
||||
* count from hitting zero before we're through
|
||||
*/
|
||||
inc_io(page);
|
||||
if (!bio->bi_iter.bi_size)
|
||||
goto dump_bio;
|
||||
submit_bio(WRITE, bio);
|
||||
nr_underway++;
|
||||
bio = NULL;
|
||||
} else
|
||||
inc_io(page);
|
||||
xlen = (PAGE_CACHE_SIZE - offset) >> inode->i_blkbits;
|
||||
pblock = metapage_get_blocks(inode, lblock, &xlen);
|
||||
if (!pblock) {
|
||||
printk(KERN_ERR "JFS: metapage_get_blocks failed\n");
|
||||
/*
|
||||
* We already called inc_io(), but can't cancel it
|
||||
* with dec_io() until we're done with the page
|
||||
*/
|
||||
bad_blocks++;
|
||||
continue;
|
||||
}
|
||||
len = min(xlen, (int)JFS_SBI(inode->i_sb)->nbperpage);
|
||||
|
||||
bio = bio_alloc(GFP_NOFS, 1);
|
||||
bio->bi_bdev = inode->i_sb->s_bdev;
|
||||
bio->bi_iter.bi_sector = pblock << (inode->i_blkbits - 9);
|
||||
bio->bi_end_io = metapage_write_end_io;
|
||||
bio->bi_private = page;
|
||||
|
||||
/* Don't call bio_add_page yet, we may add to this vec */
|
||||
bio_offset = offset;
|
||||
bio_bytes = len << inode->i_blkbits;
|
||||
|
||||
xlen -= len;
|
||||
next_block = lblock + len;
|
||||
}
|
||||
if (bio) {
|
||||
if (bio_add_page(bio, page, bio_bytes, bio_offset) < bio_bytes)
|
||||
goto add_failed;
|
||||
if (!bio->bi_iter.bi_size)
|
||||
goto dump_bio;
|
||||
|
||||
submit_bio(WRITE, bio);
|
||||
nr_underway++;
|
||||
}
|
||||
if (redirty)
|
||||
redirty_page_for_writepage(wbc, page);
|
||||
|
||||
unlock_page(page);
|
||||
|
||||
if (bad_blocks)
|
||||
goto err_out;
|
||||
|
||||
if (nr_underway == 0)
|
||||
end_page_writeback(page);
|
||||
|
||||
return 0;
|
||||
add_failed:
|
||||
/* We should never reach here, since we're only adding one vec */
|
||||
printk(KERN_ERR "JFS: bio_add_page failed unexpectedly\n");
|
||||
goto skip;
|
||||
dump_bio:
|
||||
print_hex_dump(KERN_ERR, "JFS: dump of bio: ", DUMP_PREFIX_ADDRESS, 16,
|
||||
4, bio, sizeof(*bio), 0);
|
||||
skip:
|
||||
bio_put(bio);
|
||||
unlock_page(page);
|
||||
dec_io(page, last_write_complete);
|
||||
err_out:
|
||||
while (bad_blocks--)
|
||||
dec_io(page, last_write_complete);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int metapage_readpage(struct file *fp, struct page *page)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct bio *bio = NULL;
|
||||
int block_offset;
|
||||
int blocks_per_page = PAGE_CACHE_SIZE >> inode->i_blkbits;
|
||||
sector_t page_start; /* address of page in fs blocks */
|
||||
sector_t pblock;
|
||||
int xlen;
|
||||
unsigned int len;
|
||||
int offset;
|
||||
|
||||
BUG_ON(!PageLocked(page));
|
||||
page_start = (sector_t)page->index <<
|
||||
(PAGE_CACHE_SHIFT - inode->i_blkbits);
|
||||
|
||||
block_offset = 0;
|
||||
while (block_offset < blocks_per_page) {
|
||||
xlen = blocks_per_page - block_offset;
|
||||
pblock = metapage_get_blocks(inode, page_start + block_offset,
|
||||
&xlen);
|
||||
if (pblock) {
|
||||
if (!PagePrivate(page))
|
||||
insert_metapage(page, NULL);
|
||||
inc_io(page);
|
||||
if (bio)
|
||||
submit_bio(READ, bio);
|
||||
|
||||
bio = bio_alloc(GFP_NOFS, 1);
|
||||
bio->bi_bdev = inode->i_sb->s_bdev;
|
||||
bio->bi_iter.bi_sector =
|
||||
pblock << (inode->i_blkbits - 9);
|
||||
bio->bi_end_io = metapage_read_end_io;
|
||||
bio->bi_private = page;
|
||||
len = xlen << inode->i_blkbits;
|
||||
offset = block_offset << inode->i_blkbits;
|
||||
if (bio_add_page(bio, page, len, offset) < len)
|
||||
goto add_failed;
|
||||
block_offset += xlen;
|
||||
} else
|
||||
block_offset++;
|
||||
}
|
||||
if (bio)
|
||||
submit_bio(READ, bio);
|
||||
else
|
||||
unlock_page(page);
|
||||
|
||||
return 0;
|
||||
|
||||
add_failed:
|
||||
printk(KERN_ERR "JFS: bio_add_page failed unexpectedly\n");
|
||||
bio_put(bio);
|
||||
dec_io(page, last_read_complete);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int metapage_releasepage(struct page *page, gfp_t gfp_mask)
|
||||
{
|
||||
struct metapage *mp;
|
||||
int ret = 1;
|
||||
int offset;
|
||||
|
||||
for (offset = 0; offset < PAGE_CACHE_SIZE; offset += PSIZE) {
|
||||
mp = page_to_mp(page, offset);
|
||||
|
||||
if (!mp)
|
||||
continue;
|
||||
|
||||
jfs_info("metapage_releasepage: mp = 0x%p", mp);
|
||||
if (mp->count || mp->nohomeok ||
|
||||
test_bit(META_dirty, &mp->flag)) {
|
||||
jfs_info("count = %ld, nohomeok = %d", mp->count,
|
||||
mp->nohomeok);
|
||||
ret = 0;
|
||||
continue;
|
||||
}
|
||||
if (mp->lsn)
|
||||
remove_from_logsync(mp);
|
||||
remove_metapage(page, mp);
|
||||
INCREMENT(mpStat.pagefree);
|
||||
free_metapage(mp);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void metapage_invalidatepage(struct page *page, unsigned int offset,
|
||||
unsigned int length)
|
||||
{
|
||||
BUG_ON(offset || length < PAGE_CACHE_SIZE);
|
||||
|
||||
BUG_ON(PageWriteback(page));
|
||||
|
||||
metapage_releasepage(page, 0);
|
||||
}
|
||||
|
||||
const struct address_space_operations jfs_metapage_aops = {
|
||||
.readpage = metapage_readpage,
|
||||
.writepage = metapage_writepage,
|
||||
.releasepage = metapage_releasepage,
|
||||
.invalidatepage = metapage_invalidatepage,
|
||||
.set_page_dirty = __set_page_dirty_nobuffers,
|
||||
};
|
||||
|
||||
struct metapage *__get_metapage(struct inode *inode, unsigned long lblock,
|
||||
unsigned int size, int absolute,
|
||||
unsigned long new)
|
||||
{
|
||||
int l2BlocksPerPage;
|
||||
int l2bsize;
|
||||
struct address_space *mapping;
|
||||
struct metapage *mp = NULL;
|
||||
struct page *page;
|
||||
unsigned long page_index;
|
||||
unsigned long page_offset;
|
||||
|
||||
jfs_info("__get_metapage: ino = %ld, lblock = 0x%lx, abs=%d",
|
||||
inode->i_ino, lblock, absolute);
|
||||
|
||||
l2bsize = inode->i_blkbits;
|
||||
l2BlocksPerPage = PAGE_CACHE_SHIFT - l2bsize;
|
||||
page_index = lblock >> l2BlocksPerPage;
|
||||
page_offset = (lblock - (page_index << l2BlocksPerPage)) << l2bsize;
|
||||
if ((page_offset + size) > PAGE_CACHE_SIZE) {
|
||||
jfs_err("MetaData crosses page boundary!!");
|
||||
jfs_err("lblock = %lx, size = %d", lblock, size);
|
||||
dump_stack();
|
||||
return NULL;
|
||||
}
|
||||
if (absolute)
|
||||
mapping = JFS_SBI(inode->i_sb)->direct_inode->i_mapping;
|
||||
else {
|
||||
/*
|
||||
* If an nfs client tries to read an inode that is larger
|
||||
* than any existing inodes, we may try to read past the
|
||||
* end of the inode map
|
||||
*/
|
||||
if ((lblock << inode->i_blkbits) >= inode->i_size)
|
||||
return NULL;
|
||||
mapping = inode->i_mapping;
|
||||
}
|
||||
|
||||
if (new && (PSIZE == PAGE_CACHE_SIZE)) {
|
||||
page = grab_cache_page(mapping, page_index);
|
||||
if (!page) {
|
||||
jfs_err("grab_cache_page failed!");
|
||||
return NULL;
|
||||
}
|
||||
SetPageUptodate(page);
|
||||
} else {
|
||||
page = read_mapping_page(mapping, page_index, NULL);
|
||||
if (IS_ERR(page) || !PageUptodate(page)) {
|
||||
jfs_err("read_mapping_page failed!");
|
||||
return NULL;
|
||||
}
|
||||
lock_page(page);
|
||||
}
|
||||
|
||||
mp = page_to_mp(page, page_offset);
|
||||
if (mp) {
|
||||
if (mp->logical_size != size) {
|
||||
jfs_error(inode->i_sb,
|
||||
"get_mp->logical_size != size\n");
|
||||
jfs_err("logical_size = %d, size = %d",
|
||||
mp->logical_size, size);
|
||||
dump_stack();
|
||||
goto unlock;
|
||||
}
|
||||
mp->count++;
|
||||
lock_metapage(mp);
|
||||
if (test_bit(META_discard, &mp->flag)) {
|
||||
if (!new) {
|
||||
jfs_error(inode->i_sb,
|
||||
"using a discarded metapage\n");
|
||||
discard_metapage(mp);
|
||||
goto unlock;
|
||||
}
|
||||
clear_bit(META_discard, &mp->flag);
|
||||
}
|
||||
} else {
|
||||
INCREMENT(mpStat.pagealloc);
|
||||
mp = alloc_metapage(GFP_NOFS);
|
||||
mp->page = page;
|
||||
mp->flag = 0;
|
||||
mp->xflag = COMMIT_PAGE;
|
||||
mp->count = 1;
|
||||
mp->nohomeok = 0;
|
||||
mp->logical_size = size;
|
||||
mp->data = page_address(page) + page_offset;
|
||||
mp->index = lblock;
|
||||
if (unlikely(insert_metapage(page, mp))) {
|
||||
free_metapage(mp);
|
||||
goto unlock;
|
||||
}
|
||||
lock_metapage(mp);
|
||||
}
|
||||
|
||||
if (new) {
|
||||
jfs_info("zeroing mp = 0x%p", mp);
|
||||
memset(mp->data, 0, PSIZE);
|
||||
}
|
||||
|
||||
unlock_page(page);
|
||||
jfs_info("__get_metapage: returning = 0x%p data = 0x%p", mp, mp->data);
|
||||
return mp;
|
||||
|
||||
unlock:
|
||||
unlock_page(page);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void grab_metapage(struct metapage * mp)
|
||||
{
|
||||
jfs_info("grab_metapage: mp = 0x%p", mp);
|
||||
page_cache_get(mp->page);
|
||||
lock_page(mp->page);
|
||||
mp->count++;
|
||||
lock_metapage(mp);
|
||||
unlock_page(mp->page);
|
||||
}
|
||||
|
||||
void force_metapage(struct metapage *mp)
|
||||
{
|
||||
struct page *page = mp->page;
|
||||
jfs_info("force_metapage: mp = 0x%p", mp);
|
||||
set_bit(META_forcewrite, &mp->flag);
|
||||
clear_bit(META_sync, &mp->flag);
|
||||
page_cache_get(page);
|
||||
lock_page(page);
|
||||
set_page_dirty(page);
|
||||
write_one_page(page, 1);
|
||||
clear_bit(META_forcewrite, &mp->flag);
|
||||
page_cache_release(page);
|
||||
}
|
||||
|
||||
void hold_metapage(struct metapage *mp)
|
||||
{
|
||||
lock_page(mp->page);
|
||||
}
|
||||
|
||||
void put_metapage(struct metapage *mp)
|
||||
{
|
||||
if (mp->count || mp->nohomeok) {
|
||||
/* Someone else will release this */
|
||||
unlock_page(mp->page);
|
||||
return;
|
||||
}
|
||||
page_cache_get(mp->page);
|
||||
mp->count++;
|
||||
lock_metapage(mp);
|
||||
unlock_page(mp->page);
|
||||
release_metapage(mp);
|
||||
}
|
||||
|
||||
void release_metapage(struct metapage * mp)
|
||||
{
|
||||
struct page *page = mp->page;
|
||||
jfs_info("release_metapage: mp = 0x%p, flag = 0x%lx", mp, mp->flag);
|
||||
|
||||
BUG_ON(!page);
|
||||
|
||||
lock_page(page);
|
||||
unlock_metapage(mp);
|
||||
|
||||
assert(mp->count);
|
||||
if (--mp->count || mp->nohomeok) {
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
return;
|
||||
}
|
||||
|
||||
if (test_bit(META_dirty, &mp->flag)) {
|
||||
set_page_dirty(page);
|
||||
if (test_bit(META_sync, &mp->flag)) {
|
||||
clear_bit(META_sync, &mp->flag);
|
||||
write_one_page(page, 1);
|
||||
lock_page(page); /* write_one_page unlocks the page */
|
||||
}
|
||||
} else if (mp->lsn) /* discard_metapage doesn't remove it */
|
||||
remove_from_logsync(mp);
|
||||
|
||||
/* Try to keep metapages from using up too much memory */
|
||||
drop_metapage(page, mp);
|
||||
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
}
|
||||
|
||||
void __invalidate_metapages(struct inode *ip, s64 addr, int len)
|
||||
{
|
||||
sector_t lblock;
|
||||
int l2BlocksPerPage = PAGE_CACHE_SHIFT - ip->i_blkbits;
|
||||
int BlocksPerPage = 1 << l2BlocksPerPage;
|
||||
/* All callers are interested in block device's mapping */
|
||||
struct address_space *mapping =
|
||||
JFS_SBI(ip->i_sb)->direct_inode->i_mapping;
|
||||
struct metapage *mp;
|
||||
struct page *page;
|
||||
unsigned int offset;
|
||||
|
||||
/*
|
||||
* Mark metapages to discard. They will eventually be
|
||||
* released, but should not be written.
|
||||
*/
|
||||
for (lblock = addr & ~(BlocksPerPage - 1); lblock < addr + len;
|
||||
lblock += BlocksPerPage) {
|
||||
page = find_lock_page(mapping, lblock >> l2BlocksPerPage);
|
||||
if (!page)
|
||||
continue;
|
||||
for (offset = 0; offset < PAGE_CACHE_SIZE; offset += PSIZE) {
|
||||
mp = page_to_mp(page, offset);
|
||||
if (!mp)
|
||||
continue;
|
||||
if (mp->index < addr)
|
||||
continue;
|
||||
if (mp->index >= addr + len)
|
||||
break;
|
||||
|
||||
clear_bit(META_dirty, &mp->flag);
|
||||
set_bit(META_discard, &mp->flag);
|
||||
if (mp->lsn)
|
||||
remove_from_logsync(mp);
|
||||
}
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_JFS_STATISTICS
|
||||
static int jfs_mpstat_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
seq_printf(m,
|
||||
"JFS Metapage statistics\n"
|
||||
"=======================\n"
|
||||
"page allocations = %d\n"
|
||||
"page frees = %d\n"
|
||||
"lock waits = %d\n",
|
||||
mpStat.pagealloc,
|
||||
mpStat.pagefree,
|
||||
mpStat.lockwait);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jfs_mpstat_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, jfs_mpstat_proc_show, NULL);
|
||||
}
|
||||
|
||||
const struct file_operations jfs_mpstat_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = jfs_mpstat_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
#endif
|
155
fs/jfs/jfs_metapage.h
Normal file
155
fs/jfs/jfs_metapage.h
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2002
|
||||
* Portions Copyright (C) Christoph Hellwig, 2001-2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_METAPAGE
|
||||
#define _H_JFS_METAPAGE
|
||||
|
||||
#include <linux/pagemap.h>
|
||||
|
||||
struct metapage {
|
||||
/* Common logsyncblk prefix (see jfs_logmgr.h) */
|
||||
u16 xflag;
|
||||
u16 unused;
|
||||
lid_t lid;
|
||||
int lsn;
|
||||
struct list_head synclist;
|
||||
/* End of logsyncblk prefix */
|
||||
|
||||
unsigned long flag; /* See Below */
|
||||
unsigned long count; /* Reference count */
|
||||
void *data; /* Data pointer */
|
||||
sector_t index; /* block address of page */
|
||||
wait_queue_head_t wait;
|
||||
|
||||
/* implementation */
|
||||
struct page *page;
|
||||
unsigned int logical_size;
|
||||
|
||||
/* Journal management */
|
||||
int clsn;
|
||||
int nohomeok;
|
||||
struct jfs_log *log;
|
||||
};
|
||||
|
||||
/* metapage flag */
|
||||
#define META_locked 0
|
||||
#define META_free 1
|
||||
#define META_dirty 2
|
||||
#define META_sync 3
|
||||
#define META_discard 4
|
||||
#define META_forcewrite 5
|
||||
#define META_io 6
|
||||
|
||||
#define mark_metapage_dirty(mp) set_bit(META_dirty, &(mp)->flag)
|
||||
|
||||
/* function prototypes */
|
||||
extern int metapage_init(void);
|
||||
extern void metapage_exit(void);
|
||||
extern struct metapage *__get_metapage(struct inode *inode,
|
||||
unsigned long lblock, unsigned int size,
|
||||
int absolute, unsigned long new);
|
||||
|
||||
#define read_metapage(inode, lblock, size, absolute)\
|
||||
__get_metapage(inode, lblock, size, absolute, false)
|
||||
|
||||
#define get_metapage(inode, lblock, size, absolute)\
|
||||
__get_metapage(inode, lblock, size, absolute, true)
|
||||
|
||||
extern void release_metapage(struct metapage *);
|
||||
extern void grab_metapage(struct metapage *);
|
||||
extern void force_metapage(struct metapage *);
|
||||
|
||||
/*
|
||||
* hold_metapage and put_metapage are used in conjunction. The page lock
|
||||
* is not dropped between the two, so no other threads can get or release
|
||||
* the metapage
|
||||
*/
|
||||
extern void hold_metapage(struct metapage *);
|
||||
extern void put_metapage(struct metapage *);
|
||||
|
||||
static inline void write_metapage(struct metapage *mp)
|
||||
{
|
||||
set_bit(META_dirty, &mp->flag);
|
||||
release_metapage(mp);
|
||||
}
|
||||
|
||||
static inline void flush_metapage(struct metapage *mp)
|
||||
{
|
||||
set_bit(META_sync, &mp->flag);
|
||||
write_metapage(mp);
|
||||
}
|
||||
|
||||
static inline void discard_metapage(struct metapage *mp)
|
||||
{
|
||||
clear_bit(META_dirty, &mp->flag);
|
||||
set_bit(META_discard, &mp->flag);
|
||||
release_metapage(mp);
|
||||
}
|
||||
|
||||
static inline void metapage_nohomeok(struct metapage *mp)
|
||||
{
|
||||
struct page *page = mp->page;
|
||||
lock_page(page);
|
||||
if (!mp->nohomeok++) {
|
||||
mark_metapage_dirty(mp);
|
||||
page_cache_get(page);
|
||||
wait_on_page_writeback(page);
|
||||
}
|
||||
unlock_page(page);
|
||||
}
|
||||
|
||||
/*
|
||||
* This serializes access to mp->lsn when metapages are added to logsynclist
|
||||
* without setting nohomeok. i.e. updating imap & dmap
|
||||
*/
|
||||
static inline void metapage_wait_for_io(struct metapage *mp)
|
||||
{
|
||||
if (test_bit(META_io, &mp->flag))
|
||||
wait_on_page_writeback(mp->page);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called when already holding the metapage
|
||||
*/
|
||||
static inline void _metapage_homeok(struct metapage *mp)
|
||||
{
|
||||
if (!--mp->nohomeok)
|
||||
page_cache_release(mp->page);
|
||||
}
|
||||
|
||||
static inline void metapage_homeok(struct metapage *mp)
|
||||
{
|
||||
hold_metapage(mp);
|
||||
_metapage_homeok(mp);
|
||||
put_metapage(mp);
|
||||
}
|
||||
|
||||
extern const struct address_space_operations jfs_metapage_aops;
|
||||
|
||||
/*
|
||||
* This routines invalidate all pages for an extent.
|
||||
*/
|
||||
extern void __invalidate_metapages(struct inode *, s64, int);
|
||||
#define invalidate_pxd_metapages(ip, pxd) \
|
||||
__invalidate_metapages((ip), addressPXD(&(pxd)), lengthPXD(&(pxd)))
|
||||
#define invalidate_dxd_metapages(ip, dxd) \
|
||||
__invalidate_metapages((ip), addressDXD(&(dxd)), lengthDXD(&(dxd)))
|
||||
#define invalidate_xad_metapages(ip, xad) \
|
||||
__invalidate_metapages((ip), addressXAD(&(xad)), lengthXAD(&(xad)))
|
||||
|
||||
#endif /* _H_JFS_METAPAGE */
|
507
fs/jfs/jfs_mount.c
Normal file
507
fs/jfs/jfs_mount.c
Normal file
|
@ -0,0 +1,507 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2004
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Module: jfs_mount.c
|
||||
*
|
||||
* note: file system in transition to aggregate/fileset:
|
||||
*
|
||||
* file system mount is interpreted as the mount of aggregate,
|
||||
* if not already mounted, and mount of the single/only fileset in
|
||||
* the aggregate;
|
||||
*
|
||||
* a file system/aggregate is represented by an internal inode
|
||||
* (aka mount inode) initialized with aggregate superblock;
|
||||
* each vfs represents a fileset, and points to its "fileset inode
|
||||
* allocation map inode" (aka fileset inode):
|
||||
* (an aggregate itself is structured recursively as a filset:
|
||||
* an internal vfs is constructed and points to its "fileset inode
|
||||
* allocation map inode" (aka aggregate inode) where each inode
|
||||
* represents a fileset inode) so that inode number is mapped to
|
||||
* on-disk inode in uniform way at both aggregate and fileset level;
|
||||
*
|
||||
* each vnode/inode of a fileset is linked to its vfs (to facilitate
|
||||
* per fileset inode operations, e.g., unmount of a fileset, etc.);
|
||||
* each inode points to the mount inode (to facilitate access to
|
||||
* per aggregate information, e.g., block size, etc.) as well as
|
||||
* its file set inode.
|
||||
*
|
||||
* aggregate
|
||||
* ipmnt
|
||||
* mntvfs -> fileset ipimap+ -> aggregate ipbmap -> aggregate ipaimap;
|
||||
* fileset vfs -> vp(1) <-> ... <-> vp(n) <->vproot;
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/buffer_head.h>
|
||||
|
||||
#include "jfs_incore.h"
|
||||
#include "jfs_filsys.h"
|
||||
#include "jfs_superblock.h"
|
||||
#include "jfs_dmap.h"
|
||||
#include "jfs_imap.h"
|
||||
#include "jfs_metapage.h"
|
||||
#include "jfs_debug.h"
|
||||
|
||||
|
||||
/*
|
||||
* forward references
|
||||
*/
|
||||
static int chkSuper(struct super_block *);
|
||||
static int logMOUNT(struct super_block *sb);
|
||||
|
||||
/*
|
||||
* NAME: jfs_mount(sb)
|
||||
*
|
||||
* FUNCTION: vfs_mount()
|
||||
*
|
||||
* PARAMETER: sb - super block
|
||||
*
|
||||
* RETURN: -EBUSY - device already mounted or open for write
|
||||
* -EBUSY - cvrdvp already mounted;
|
||||
* -EBUSY - mount table full
|
||||
* -ENOTDIR- cvrdvp not directory on a device mount
|
||||
* -ENXIO - device open failure
|
||||
*/
|
||||
int jfs_mount(struct super_block *sb)
|
||||
{
|
||||
int rc = 0; /* Return code */
|
||||
struct jfs_sb_info *sbi = JFS_SBI(sb);
|
||||
struct inode *ipaimap = NULL;
|
||||
struct inode *ipaimap2 = NULL;
|
||||
struct inode *ipimap = NULL;
|
||||
struct inode *ipbmap = NULL;
|
||||
|
||||
/*
|
||||
* read/validate superblock
|
||||
* (initialize mount inode from the superblock)
|
||||
*/
|
||||
if ((rc = chkSuper(sb))) {
|
||||
goto errout20;
|
||||
}
|
||||
|
||||
ipaimap = diReadSpecial(sb, AGGREGATE_I, 0);
|
||||
if (ipaimap == NULL) {
|
||||
jfs_err("jfs_mount: Failed to read AGGREGATE_I");
|
||||
rc = -EIO;
|
||||
goto errout20;
|
||||
}
|
||||
sbi->ipaimap = ipaimap;
|
||||
|
||||
jfs_info("jfs_mount: ipaimap:0x%p", ipaimap);
|
||||
|
||||
/*
|
||||
* initialize aggregate inode allocation map
|
||||
*/
|
||||
if ((rc = diMount(ipaimap))) {
|
||||
jfs_err("jfs_mount: diMount(ipaimap) failed w/rc = %d", rc);
|
||||
goto errout21;
|
||||
}
|
||||
|
||||
/*
|
||||
* open aggregate block allocation map
|
||||
*/
|
||||
ipbmap = diReadSpecial(sb, BMAP_I, 0);
|
||||
if (ipbmap == NULL) {
|
||||
rc = -EIO;
|
||||
goto errout22;
|
||||
}
|
||||
|
||||
jfs_info("jfs_mount: ipbmap:0x%p", ipbmap);
|
||||
|
||||
sbi->ipbmap = ipbmap;
|
||||
|
||||
/*
|
||||
* initialize aggregate block allocation map
|
||||
*/
|
||||
if ((rc = dbMount(ipbmap))) {
|
||||
jfs_err("jfs_mount: dbMount failed w/rc = %d", rc);
|
||||
goto errout22;
|
||||
}
|
||||
|
||||
/*
|
||||
* open the secondary aggregate inode allocation map
|
||||
*
|
||||
* This is a duplicate of the aggregate inode allocation map.
|
||||
*
|
||||
* hand craft a vfs in the same fashion as we did to read ipaimap.
|
||||
* By adding INOSPEREXT (32) to the inode number, we are telling
|
||||
* diReadSpecial that we are reading from the secondary aggregate
|
||||
* inode table. This also creates a unique entry in the inode hash
|
||||
* table.
|
||||
*/
|
||||
if ((sbi->mntflag & JFS_BAD_SAIT) == 0) {
|
||||
ipaimap2 = diReadSpecial(sb, AGGREGATE_I, 1);
|
||||
if (!ipaimap2) {
|
||||
jfs_err("jfs_mount: Failed to read AGGREGATE_I");
|
||||
rc = -EIO;
|
||||
goto errout35;
|
||||
}
|
||||
sbi->ipaimap2 = ipaimap2;
|
||||
|
||||
jfs_info("jfs_mount: ipaimap2:0x%p", ipaimap2);
|
||||
|
||||
/*
|
||||
* initialize secondary aggregate inode allocation map
|
||||
*/
|
||||
if ((rc = diMount(ipaimap2))) {
|
||||
jfs_err("jfs_mount: diMount(ipaimap2) failed, rc = %d",
|
||||
rc);
|
||||
goto errout35;
|
||||
}
|
||||
} else
|
||||
/* Secondary aggregate inode table is not valid */
|
||||
sbi->ipaimap2 = NULL;
|
||||
|
||||
/*
|
||||
* mount (the only/single) fileset
|
||||
*/
|
||||
/*
|
||||
* open fileset inode allocation map (aka fileset inode)
|
||||
*/
|
||||
ipimap = diReadSpecial(sb, FILESYSTEM_I, 0);
|
||||
if (ipimap == NULL) {
|
||||
jfs_err("jfs_mount: Failed to read FILESYSTEM_I");
|
||||
/* open fileset secondary inode allocation map */
|
||||
rc = -EIO;
|
||||
goto errout40;
|
||||
}
|
||||
jfs_info("jfs_mount: ipimap:0x%p", ipimap);
|
||||
|
||||
/* map further access of per fileset inodes by the fileset inode */
|
||||
sbi->ipimap = ipimap;
|
||||
|
||||
/* initialize fileset inode allocation map */
|
||||
if ((rc = diMount(ipimap))) {
|
||||
jfs_err("jfs_mount: diMount failed w/rc = %d", rc);
|
||||
goto errout41;
|
||||
}
|
||||
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* unwind on error
|
||||
*/
|
||||
errout41: /* close fileset inode allocation map inode */
|
||||
diFreeSpecial(ipimap);
|
||||
|
||||
errout40: /* fileset closed */
|
||||
|
||||
/* close secondary aggregate inode allocation map */
|
||||
if (ipaimap2) {
|
||||
diUnmount(ipaimap2, 1);
|
||||
diFreeSpecial(ipaimap2);
|
||||
}
|
||||
|
||||
errout35:
|
||||
|
||||
/* close aggregate block allocation map */
|
||||
dbUnmount(ipbmap, 1);
|
||||
diFreeSpecial(ipbmap);
|
||||
|
||||
errout22: /* close aggregate inode allocation map */
|
||||
|
||||
diUnmount(ipaimap, 1);
|
||||
|
||||
errout21: /* close aggregate inodes */
|
||||
diFreeSpecial(ipaimap);
|
||||
errout20: /* aggregate closed */
|
||||
|
||||
out:
|
||||
|
||||
if (rc)
|
||||
jfs_err("Mount JFS Failure: %d", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: jfs_mount_rw(sb, remount)
|
||||
*
|
||||
* FUNCTION: Completes read-write mount, or remounts read-only volume
|
||||
* as read-write
|
||||
*/
|
||||
int jfs_mount_rw(struct super_block *sb, int remount)
|
||||
{
|
||||
struct jfs_sb_info *sbi = JFS_SBI(sb);
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* If we are re-mounting a previously read-only volume, we want to
|
||||
* re-read the inode and block maps, since fsck.jfs may have updated
|
||||
* them.
|
||||
*/
|
||||
if (remount) {
|
||||
if (chkSuper(sb) || (sbi->state != FM_CLEAN))
|
||||
return -EINVAL;
|
||||
|
||||
truncate_inode_pages(sbi->ipimap->i_mapping, 0);
|
||||
truncate_inode_pages(sbi->ipbmap->i_mapping, 0);
|
||||
diUnmount(sbi->ipimap, 1);
|
||||
if ((rc = diMount(sbi->ipimap))) {
|
||||
jfs_err("jfs_mount_rw: diMount failed!");
|
||||
return rc;
|
||||
}
|
||||
|
||||
dbUnmount(sbi->ipbmap, 1);
|
||||
if ((rc = dbMount(sbi->ipbmap))) {
|
||||
jfs_err("jfs_mount_rw: dbMount failed!");
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* open/initialize log
|
||||
*/
|
||||
if ((rc = lmLogOpen(sb)))
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* update file system superblock;
|
||||
*/
|
||||
if ((rc = updateSuper(sb, FM_MOUNT))) {
|
||||
jfs_err("jfs_mount: updateSuper failed w/rc = %d", rc);
|
||||
lmLogClose(sb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* write MOUNT log record of the file system
|
||||
*/
|
||||
logMOUNT(sb);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* chkSuper()
|
||||
*
|
||||
* validate the superblock of the file system to be mounted and
|
||||
* get the file system parameters.
|
||||
*
|
||||
* returns
|
||||
* 0 with fragsize set if check successful
|
||||
* error code if not successful
|
||||
*/
|
||||
static int chkSuper(struct super_block *sb)
|
||||
{
|
||||
int rc = 0;
|
||||
struct jfs_sb_info *sbi = JFS_SBI(sb);
|
||||
struct jfs_superblock *j_sb;
|
||||
struct buffer_head *bh;
|
||||
int AIM_bytesize, AIT_bytesize;
|
||||
int expected_AIM_bytesize, expected_AIT_bytesize;
|
||||
s64 AIM_byte_addr, AIT_byte_addr, fsckwsp_addr;
|
||||
s64 byte_addr_diff0, byte_addr_diff1;
|
||||
s32 bsize;
|
||||
|
||||
if ((rc = readSuper(sb, &bh)))
|
||||
return rc;
|
||||
j_sb = (struct jfs_superblock *)bh->b_data;
|
||||
|
||||
/*
|
||||
* validate superblock
|
||||
*/
|
||||
/* validate fs signature */
|
||||
if (strncmp(j_sb->s_magic, JFS_MAGIC, 4) ||
|
||||
le32_to_cpu(j_sb->s_version) > JFS_VERSION) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bsize = le32_to_cpu(j_sb->s_bsize);
|
||||
#ifdef _JFS_4K
|
||||
if (bsize != PSIZE) {
|
||||
jfs_err("Currently only 4K block size supported!");
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
#endif /* _JFS_4K */
|
||||
|
||||
jfs_info("superblock: flag:0x%08x state:0x%08x size:0x%Lx",
|
||||
le32_to_cpu(j_sb->s_flag), le32_to_cpu(j_sb->s_state),
|
||||
(unsigned long long) le64_to_cpu(j_sb->s_size));
|
||||
|
||||
/* validate the descriptors for Secondary AIM and AIT */
|
||||
if ((j_sb->s_flag & cpu_to_le32(JFS_BAD_SAIT)) !=
|
||||
cpu_to_le32(JFS_BAD_SAIT)) {
|
||||
expected_AIM_bytesize = 2 * PSIZE;
|
||||
AIM_bytesize = lengthPXD(&(j_sb->s_aim2)) * bsize;
|
||||
expected_AIT_bytesize = 4 * PSIZE;
|
||||
AIT_bytesize = lengthPXD(&(j_sb->s_ait2)) * bsize;
|
||||
AIM_byte_addr = addressPXD(&(j_sb->s_aim2)) * bsize;
|
||||
AIT_byte_addr = addressPXD(&(j_sb->s_ait2)) * bsize;
|
||||
byte_addr_diff0 = AIT_byte_addr - AIM_byte_addr;
|
||||
fsckwsp_addr = addressPXD(&(j_sb->s_fsckpxd)) * bsize;
|
||||
byte_addr_diff1 = fsckwsp_addr - AIT_byte_addr;
|
||||
if ((AIM_bytesize != expected_AIM_bytesize) ||
|
||||
(AIT_bytesize != expected_AIT_bytesize) ||
|
||||
(byte_addr_diff0 != AIM_bytesize) ||
|
||||
(byte_addr_diff1 <= AIT_bytesize))
|
||||
j_sb->s_flag |= cpu_to_le32(JFS_BAD_SAIT);
|
||||
}
|
||||
|
||||
if ((j_sb->s_flag & cpu_to_le32(JFS_GROUPCOMMIT)) !=
|
||||
cpu_to_le32(JFS_GROUPCOMMIT))
|
||||
j_sb->s_flag |= cpu_to_le32(JFS_GROUPCOMMIT);
|
||||
|
||||
/* validate fs state */
|
||||
if (j_sb->s_state != cpu_to_le32(FM_CLEAN) &&
|
||||
!(sb->s_flags & MS_RDONLY)) {
|
||||
jfs_err("jfs_mount: Mount Failure: File System Dirty.");
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sbi->state = le32_to_cpu(j_sb->s_state);
|
||||
sbi->mntflag = le32_to_cpu(j_sb->s_flag);
|
||||
|
||||
/*
|
||||
* JFS always does I/O by 4K pages. Don't tell the buffer cache
|
||||
* that we use anything else (leave s_blocksize alone).
|
||||
*/
|
||||
sbi->bsize = bsize;
|
||||
sbi->l2bsize = le16_to_cpu(j_sb->s_l2bsize);
|
||||
|
||||
/*
|
||||
* For now, ignore s_pbsize, l2bfactor. All I/O going through buffer
|
||||
* cache.
|
||||
*/
|
||||
sbi->nbperpage = PSIZE >> sbi->l2bsize;
|
||||
sbi->l2nbperpage = L2PSIZE - sbi->l2bsize;
|
||||
sbi->l2niperblk = sbi->l2bsize - L2DISIZE;
|
||||
if (sbi->mntflag & JFS_INLINELOG)
|
||||
sbi->logpxd = j_sb->s_logpxd;
|
||||
else {
|
||||
sbi->logdev = new_decode_dev(le32_to_cpu(j_sb->s_logdev));
|
||||
memcpy(sbi->uuid, j_sb->s_uuid, sizeof(sbi->uuid));
|
||||
memcpy(sbi->loguuid, j_sb->s_loguuid, sizeof(sbi->uuid));
|
||||
}
|
||||
sbi->fsckpxd = j_sb->s_fsckpxd;
|
||||
sbi->ait2 = j_sb->s_ait2;
|
||||
|
||||
out:
|
||||
brelse(bh);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* updateSuper()
|
||||
*
|
||||
* update synchronously superblock if it is mounted read-write.
|
||||
*/
|
||||
int updateSuper(struct super_block *sb, uint state)
|
||||
{
|
||||
struct jfs_superblock *j_sb;
|
||||
struct jfs_sb_info *sbi = JFS_SBI(sb);
|
||||
struct buffer_head *bh;
|
||||
int rc;
|
||||
|
||||
if (sbi->flag & JFS_NOINTEGRITY) {
|
||||
if (state == FM_DIRTY) {
|
||||
sbi->p_state = state;
|
||||
return 0;
|
||||
} else if (state == FM_MOUNT) {
|
||||
sbi->p_state = sbi->state;
|
||||
state = FM_DIRTY;
|
||||
} else if (state == FM_CLEAN) {
|
||||
state = sbi->p_state;
|
||||
} else
|
||||
jfs_err("updateSuper: bad state");
|
||||
} else if (sbi->state == FM_DIRTY)
|
||||
return 0;
|
||||
|
||||
if ((rc = readSuper(sb, &bh)))
|
||||
return rc;
|
||||
|
||||
j_sb = (struct jfs_superblock *)bh->b_data;
|
||||
|
||||
j_sb->s_state = cpu_to_le32(state);
|
||||
sbi->state = state;
|
||||
|
||||
if (state == FM_MOUNT) {
|
||||
/* record log's dev_t and mount serial number */
|
||||
j_sb->s_logdev = cpu_to_le32(new_encode_dev(sbi->log->bdev->bd_dev));
|
||||
j_sb->s_logserial = cpu_to_le32(sbi->log->serial);
|
||||
} else if (state == FM_CLEAN) {
|
||||
/*
|
||||
* If this volume is shared with OS/2, OS/2 will need to
|
||||
* recalculate DASD usage, since we don't deal with it.
|
||||
*/
|
||||
if (j_sb->s_flag & cpu_to_le32(JFS_DASD_ENABLED))
|
||||
j_sb->s_flag |= cpu_to_le32(JFS_DASD_PRIME);
|
||||
}
|
||||
|
||||
mark_buffer_dirty(bh);
|
||||
sync_dirty_buffer(bh);
|
||||
brelse(bh);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* readSuper()
|
||||
*
|
||||
* read superblock by raw sector address
|
||||
*/
|
||||
int readSuper(struct super_block *sb, struct buffer_head **bpp)
|
||||
{
|
||||
/* read in primary superblock */
|
||||
*bpp = sb_bread(sb, SUPER1_OFF >> sb->s_blocksize_bits);
|
||||
if (*bpp)
|
||||
return 0;
|
||||
|
||||
/* read in secondary/replicated superblock */
|
||||
*bpp = sb_bread(sb, SUPER2_OFF >> sb->s_blocksize_bits);
|
||||
if (*bpp)
|
||||
return 0;
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* logMOUNT()
|
||||
*
|
||||
* function: write a MOUNT log record for file system.
|
||||
*
|
||||
* MOUNT record keeps logredo() from processing log records
|
||||
* for this file system past this point in log.
|
||||
* it is harmless if mount fails.
|
||||
*
|
||||
* note: MOUNT record is at aggregate level, not at fileset level,
|
||||
* since log records of previous mounts of a fileset
|
||||
* (e.g., AFTER record of extent allocation) have to be processed
|
||||
* to update block allocation map at aggregate level.
|
||||
*/
|
||||
static int logMOUNT(struct super_block *sb)
|
||||
{
|
||||
struct jfs_log *log = JFS_SBI(sb)->log;
|
||||
struct lrd lrd;
|
||||
|
||||
lrd.logtid = 0;
|
||||
lrd.backchain = 0;
|
||||
lrd.type = cpu_to_le16(LOG_MOUNT);
|
||||
lrd.length = 0;
|
||||
lrd.aggregate = cpu_to_le32(new_encode_dev(sb->s_bdev->bd_dev));
|
||||
lmLog(log, NULL, &lrd, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
122
fs/jfs/jfs_superblock.h
Normal file
122
fs/jfs/jfs_superblock.h
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2003
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_SUPERBLOCK
|
||||
#define _H_JFS_SUPERBLOCK
|
||||
|
||||
/*
|
||||
* make the magic number something a human could read
|
||||
*/
|
||||
#define JFS_MAGIC "JFS1" /* Magic word */
|
||||
|
||||
#define JFS_VERSION 2 /* Version number: Version 2 */
|
||||
|
||||
#define LV_NAME_SIZE 11 /* MUST BE 11 for OS/2 boot sector */
|
||||
|
||||
/*
|
||||
* aggregate superblock
|
||||
*
|
||||
* The name superblock is too close to super_block, so the name has been
|
||||
* changed to jfs_superblock. The utilities are still using the old name.
|
||||
*/
|
||||
struct jfs_superblock {
|
||||
char s_magic[4]; /* 4: magic number */
|
||||
__le32 s_version; /* 4: version number */
|
||||
|
||||
__le64 s_size; /* 8: aggregate size in hardware/LVM blocks;
|
||||
* VFS: number of blocks
|
||||
*/
|
||||
__le32 s_bsize; /* 4: aggregate block size in bytes;
|
||||
* VFS: fragment size
|
||||
*/
|
||||
__le16 s_l2bsize; /* 2: log2 of s_bsize */
|
||||
__le16 s_l2bfactor; /* 2: log2(s_bsize/hardware block size) */
|
||||
__le32 s_pbsize; /* 4: hardware/LVM block size in bytes */
|
||||
__le16 s_l2pbsize; /* 2: log2 of s_pbsize */
|
||||
__le16 pad; /* 2: padding necessary for alignment */
|
||||
|
||||
__le32 s_agsize; /* 4: allocation group size in aggr. blocks */
|
||||
|
||||
__le32 s_flag; /* 4: aggregate attributes:
|
||||
* see jfs_filsys.h
|
||||
*/
|
||||
__le32 s_state; /* 4: mount/unmount/recovery state:
|
||||
* see jfs_filsys.h
|
||||
*/
|
||||
__le32 s_compress; /* 4: > 0 if data compression */
|
||||
|
||||
pxd_t s_ait2; /* 8: first extent of secondary
|
||||
* aggregate inode table
|
||||
*/
|
||||
|
||||
pxd_t s_aim2; /* 8: first extent of secondary
|
||||
* aggregate inode map
|
||||
*/
|
||||
__le32 s_logdev; /* 4: device address of log */
|
||||
__le32 s_logserial; /* 4: log serial number at aggregate mount */
|
||||
pxd_t s_logpxd; /* 8: inline log extent */
|
||||
|
||||
pxd_t s_fsckpxd; /* 8: inline fsck work space extent */
|
||||
|
||||
struct timestruc_t s_time; /* 8: time last updated */
|
||||
|
||||
__le32 s_fsckloglen; /* 4: Number of filesystem blocks reserved for
|
||||
* the fsck service log.
|
||||
* N.B. These blocks are divided among the
|
||||
* versions kept. This is not a per
|
||||
* version size.
|
||||
* N.B. These blocks are included in the
|
||||
* length field of s_fsckpxd.
|
||||
*/
|
||||
s8 s_fscklog; /* 1: which fsck service log is most recent
|
||||
* 0 => no service log data yet
|
||||
* 1 => the first one
|
||||
* 2 => the 2nd one
|
||||
*/
|
||||
char s_fpack[11]; /* 11: file system volume name
|
||||
* N.B. This must be 11 bytes to
|
||||
* conform with the OS/2 BootSector
|
||||
* requirements
|
||||
* Only used when s_version is 1
|
||||
*/
|
||||
|
||||
/* extendfs() parameter under s_state & FM_EXTENDFS */
|
||||
__le64 s_xsize; /* 8: extendfs s_size */
|
||||
pxd_t s_xfsckpxd; /* 8: extendfs fsckpxd */
|
||||
pxd_t s_xlogpxd; /* 8: extendfs logpxd */
|
||||
/* - 128 byte boundary - */
|
||||
|
||||
char s_uuid[16]; /* 16: 128-bit uuid for volume */
|
||||
char s_label[16]; /* 16: volume label */
|
||||
char s_loguuid[16]; /* 16: 128-bit uuid for log device */
|
||||
|
||||
};
|
||||
|
||||
extern int readSuper(struct super_block *, struct buffer_head **);
|
||||
extern int updateSuper(struct super_block *, uint);
|
||||
__printf(2, 3)
|
||||
extern void jfs_error(struct super_block *, const char *, ...);
|
||||
extern int jfs_mount(struct super_block *);
|
||||
extern int jfs_mount_rw(struct super_block *, int);
|
||||
extern int jfs_umount(struct super_block *);
|
||||
extern int jfs_umount_rw(struct super_block *);
|
||||
extern int jfs_extendfs(struct super_block *, s64, int);
|
||||
|
||||
extern struct task_struct *jfsIOthread;
|
||||
extern struct task_struct *jfsSyncThread;
|
||||
|
||||
#endif /*_H_JFS_SUPERBLOCK */
|
3093
fs/jfs/jfs_txnmgr.c
Normal file
3093
fs/jfs/jfs_txnmgr.c
Normal file
File diff suppressed because it is too large
Load diff
311
fs/jfs/jfs_txnmgr.h
Normal file
311
fs/jfs/jfs_txnmgr.h
Normal file
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2004
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_TXNMGR
|
||||
#define _H_JFS_TXNMGR
|
||||
|
||||
#include "jfs_logmgr.h"
|
||||
|
||||
/*
|
||||
* Hide implementation of TxBlock and TxLock
|
||||
*/
|
||||
#define tid_to_tblock(tid) (&TxBlock[tid])
|
||||
|
||||
#define lid_to_tlock(lid) (&TxLock[lid])
|
||||
|
||||
/*
|
||||
* transaction block
|
||||
*/
|
||||
struct tblock {
|
||||
/*
|
||||
* tblock and jbuf_t common area: struct logsyncblk
|
||||
*
|
||||
* the following 5 fields are the same as struct logsyncblk
|
||||
* which is common to tblock and jbuf to form logsynclist
|
||||
*/
|
||||
u16 xflag; /* tx commit type */
|
||||
u16 flag; /* tx commit state */
|
||||
lid_t dummy; /* Must keep structures common */
|
||||
s32 lsn; /* recovery lsn */
|
||||
struct list_head synclist; /* logsynclist link */
|
||||
|
||||
/* lock management */
|
||||
struct super_block *sb; /* super block */
|
||||
lid_t next; /* index of first tlock of tid */
|
||||
lid_t last; /* index of last tlock of tid */
|
||||
wait_queue_head_t waitor; /* tids waiting on this tid */
|
||||
|
||||
/* log management */
|
||||
u32 logtid; /* log transaction id */
|
||||
|
||||
/* commit management */
|
||||
struct list_head cqueue; /* commit queue list */
|
||||
s32 clsn; /* commit lsn */
|
||||
struct lbuf *bp;
|
||||
s32 pn; /* commit record log page number */
|
||||
s32 eor; /* commit record eor */
|
||||
wait_queue_head_t gcwait; /* group commit event list:
|
||||
* ready transactions wait on this
|
||||
* event for group commit completion.
|
||||
*/
|
||||
union {
|
||||
struct inode *ip; /* inode being deleted */
|
||||
pxd_t ixpxd; /* pxd of inode extent for created inode */
|
||||
} u;
|
||||
u32 ino; /* inode number being created */
|
||||
};
|
||||
|
||||
extern struct tblock *TxBlock; /* transaction block table */
|
||||
|
||||
/* commit flags: tblk->xflag */
|
||||
#define COMMIT_SYNC 0x0001 /* synchronous commit */
|
||||
#define COMMIT_FORCE 0x0002 /* force pageout at end of commit */
|
||||
#define COMMIT_FLUSH 0x0004 /* init flush at end of commit */
|
||||
#define COMMIT_MAP 0x00f0
|
||||
#define COMMIT_PMAP 0x0010 /* update pmap */
|
||||
#define COMMIT_WMAP 0x0020 /* update wmap */
|
||||
#define COMMIT_PWMAP 0x0040 /* update pwmap */
|
||||
#define COMMIT_FREE 0x0f00
|
||||
#define COMMIT_DELETE 0x0100 /* inode delete */
|
||||
#define COMMIT_TRUNCATE 0x0200 /* file truncation */
|
||||
#define COMMIT_CREATE 0x0400 /* inode create */
|
||||
#define COMMIT_LAZY 0x0800 /* lazy commit */
|
||||
#define COMMIT_PAGE 0x1000 /* Identifies element as metapage */
|
||||
#define COMMIT_INODE 0x2000 /* Identifies element as inode */
|
||||
|
||||
/* group commit flags tblk->flag: see jfs_logmgr.h */
|
||||
|
||||
/*
|
||||
* transaction lock
|
||||
*/
|
||||
struct tlock {
|
||||
lid_t next; /* 2: index next lockword on tid locklist
|
||||
* next lockword on freelist
|
||||
*/
|
||||
tid_t tid; /* 2: transaction id holding lock */
|
||||
|
||||
u16 flag; /* 2: lock control */
|
||||
u16 type; /* 2: log type */
|
||||
|
||||
struct metapage *mp; /* 4/8: object page buffer locked */
|
||||
struct inode *ip; /* 4/8: object */
|
||||
/* (16) */
|
||||
|
||||
s16 lock[24]; /* 48: overlay area */
|
||||
}; /* (64) */
|
||||
|
||||
extern struct tlock *TxLock; /* transaction lock table */
|
||||
|
||||
/*
|
||||
* tlock flag
|
||||
*/
|
||||
/* txLock state */
|
||||
#define tlckPAGELOCK 0x8000
|
||||
#define tlckINODELOCK 0x4000
|
||||
#define tlckLINELOCK 0x2000
|
||||
#define tlckINLINELOCK 0x1000
|
||||
/* lmLog state */
|
||||
#define tlckLOG 0x0800
|
||||
/* updateMap state */
|
||||
#define tlckUPDATEMAP 0x0080
|
||||
#define tlckDIRECTORY 0x0040
|
||||
/* freeLock state */
|
||||
#define tlckFREELOCK 0x0008
|
||||
#define tlckWRITEPAGE 0x0004
|
||||
#define tlckFREEPAGE 0x0002
|
||||
|
||||
/*
|
||||
* tlock type
|
||||
*/
|
||||
#define tlckTYPE 0xfe00
|
||||
#define tlckINODE 0x8000
|
||||
#define tlckXTREE 0x4000
|
||||
#define tlckDTREE 0x2000
|
||||
#define tlckMAP 0x1000
|
||||
#define tlckEA 0x0800
|
||||
#define tlckACL 0x0400
|
||||
#define tlckDATA 0x0200
|
||||
#define tlckBTROOT 0x0100
|
||||
|
||||
#define tlckOPERATION 0x00ff
|
||||
#define tlckGROW 0x0001 /* file grow */
|
||||
#define tlckREMOVE 0x0002 /* file delete */
|
||||
#define tlckTRUNCATE 0x0004 /* file truncate */
|
||||
#define tlckRELOCATE 0x0008 /* file/directory relocate */
|
||||
#define tlckENTRY 0x0001 /* directory insert/delete */
|
||||
#define tlckEXTEND 0x0002 /* directory extend in-line */
|
||||
#define tlckSPLIT 0x0010 /* splited page */
|
||||
#define tlckNEW 0x0020 /* new page from split */
|
||||
#define tlckFREE 0x0040 /* free page */
|
||||
#define tlckRELINK 0x0080 /* update sibling pointer */
|
||||
|
||||
/*
|
||||
* linelock for lmLog()
|
||||
*
|
||||
* note: linelock and its variations are overlaid
|
||||
* at tlock.lock: watch for alignment;
|
||||
*/
|
||||
struct lv {
|
||||
u8 offset; /* 1: */
|
||||
u8 length; /* 1: */
|
||||
}; /* (2) */
|
||||
|
||||
#define TLOCKSHORT 20
|
||||
#define TLOCKLONG 28
|
||||
|
||||
struct linelock {
|
||||
lid_t next; /* 2: next linelock */
|
||||
|
||||
s8 maxcnt; /* 1: */
|
||||
s8 index; /* 1: */
|
||||
|
||||
u16 flag; /* 2: */
|
||||
u8 type; /* 1: */
|
||||
u8 l2linesize; /* 1: log2 of linesize */
|
||||
/* (8) */
|
||||
|
||||
struct lv lv[20]; /* 40: */
|
||||
}; /* (48) */
|
||||
|
||||
#define dt_lock linelock
|
||||
|
||||
struct xtlock {
|
||||
lid_t next; /* 2: */
|
||||
|
||||
s8 maxcnt; /* 1: */
|
||||
s8 index; /* 1: */
|
||||
|
||||
u16 flag; /* 2: */
|
||||
u8 type; /* 1: */
|
||||
u8 l2linesize; /* 1: log2 of linesize */
|
||||
/* (8) */
|
||||
|
||||
struct lv header; /* 2: */
|
||||
struct lv lwm; /* 2: low water mark */
|
||||
struct lv hwm; /* 2: high water mark */
|
||||
struct lv twm; /* 2: */
|
||||
/* (16) */
|
||||
|
||||
s32 pxdlock[8]; /* 32: */
|
||||
}; /* (48) */
|
||||
|
||||
|
||||
/*
|
||||
* maplock for txUpdateMap()
|
||||
*
|
||||
* note: maplock and its variations are overlaid
|
||||
* at tlock.lock/linelock: watch for alignment;
|
||||
* N.B. next field may be set by linelock, and should not
|
||||
* be modified by maplock;
|
||||
* N.B. index of the first pxdlock specifies index of next
|
||||
* free maplock (i.e., number of maplock) in the tlock;
|
||||
*/
|
||||
struct maplock {
|
||||
lid_t next; /* 2: */
|
||||
|
||||
u8 maxcnt; /* 2: */
|
||||
u8 index; /* 2: next free maplock index */
|
||||
|
||||
u16 flag; /* 2: */
|
||||
u8 type; /* 1: */
|
||||
u8 count; /* 1: number of pxd/xad */
|
||||
/* (8) */
|
||||
|
||||
pxd_t pxd; /* 8: */
|
||||
}; /* (16): */
|
||||
|
||||
/* maplock flag */
|
||||
#define mlckALLOC 0x00f0
|
||||
#define mlckALLOCXADLIST 0x0080
|
||||
#define mlckALLOCPXDLIST 0x0040
|
||||
#define mlckALLOCXAD 0x0020
|
||||
#define mlckALLOCPXD 0x0010
|
||||
#define mlckFREE 0x000f
|
||||
#define mlckFREEXADLIST 0x0008
|
||||
#define mlckFREEPXDLIST 0x0004
|
||||
#define mlckFREEXAD 0x0002
|
||||
#define mlckFREEPXD 0x0001
|
||||
|
||||
#define pxd_lock maplock
|
||||
|
||||
struct xdlistlock {
|
||||
lid_t next; /* 2: */
|
||||
|
||||
u8 maxcnt; /* 2: */
|
||||
u8 index; /* 2: */
|
||||
|
||||
u16 flag; /* 2: */
|
||||
u8 type; /* 1: */
|
||||
u8 count; /* 1: number of pxd/xad */
|
||||
/* (8) */
|
||||
|
||||
/*
|
||||
* We need xdlist to be 64 bits (8 bytes), regardless of
|
||||
* whether void * is 32 or 64 bits
|
||||
*/
|
||||
union {
|
||||
void *_xdlist; /* pxd/xad list */
|
||||
s64 pad; /* 8: Force 64-bit xdlist size */
|
||||
} union64;
|
||||
}; /* (16): */
|
||||
|
||||
#define xdlist union64._xdlist
|
||||
|
||||
/*
|
||||
* commit
|
||||
*
|
||||
* parameter to the commit manager routines
|
||||
*/
|
||||
struct commit {
|
||||
tid_t tid; /* tid = index of tblock */
|
||||
int flag; /* flags */
|
||||
struct jfs_log *log; /* log */
|
||||
struct super_block *sb; /* superblock */
|
||||
|
||||
int nip; /* number of entries in iplist */
|
||||
struct inode **iplist; /* list of pointers to inodes */
|
||||
|
||||
/* log record descriptor on 64-bit boundary */
|
||||
struct lrd lrd; /* : log record descriptor */
|
||||
};
|
||||
|
||||
/*
|
||||
* external declarations
|
||||
*/
|
||||
extern int jfs_tlocks_low;
|
||||
|
||||
extern int txInit(void);
|
||||
extern void txExit(void);
|
||||
extern struct tlock *txLock(tid_t, struct inode *, struct metapage *, int);
|
||||
extern struct tlock *txMaplock(tid_t, struct inode *, int);
|
||||
extern int txCommit(tid_t, int, struct inode **, int);
|
||||
extern tid_t txBegin(struct super_block *, int);
|
||||
extern void txBeginAnon(struct super_block *);
|
||||
extern void txEnd(tid_t);
|
||||
extern void txAbort(tid_t, int);
|
||||
extern struct linelock *txLinelock(struct linelock *);
|
||||
extern void txFreeMap(struct inode *, struct maplock *, struct tblock *, int);
|
||||
extern void txEA(tid_t, struct inode *, dxd_t *, dxd_t *);
|
||||
extern void txFreelock(struct inode *);
|
||||
extern int lmLog(struct jfs_log *, struct tblock *, struct lrd *,
|
||||
struct tlock *);
|
||||
extern void txQuiesce(struct super_block *);
|
||||
extern void txResume(struct super_block *);
|
||||
extern void txLazyUnlock(struct tblock *);
|
||||
extern int jfs_lazycommit(void *);
|
||||
extern int jfs_sync(void *);
|
||||
#endif /* _H_JFS_TXNMGR */
|
159
fs/jfs/jfs_types.h
Normal file
159
fs/jfs/jfs_types.h
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2004
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_TYPES
|
||||
#define _H_JFS_TYPES
|
||||
|
||||
/*
|
||||
* jfs_types.h:
|
||||
*
|
||||
* basic type/utility definitions
|
||||
*
|
||||
* note: this header file must be the 1st include file
|
||||
* of JFS include list in all JFS .c file.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/nls.h>
|
||||
|
||||
#include "endian24.h"
|
||||
|
||||
/*
|
||||
* transaction and lock id's
|
||||
*
|
||||
* Don't change these without carefully considering the impact on the
|
||||
* size and alignment of all of the linelock variants
|
||||
*/
|
||||
typedef u16 tid_t;
|
||||
typedef u16 lid_t;
|
||||
|
||||
/*
|
||||
* Almost identical to Linux's timespec, but not quite
|
||||
*/
|
||||
struct timestruc_t {
|
||||
__le32 tv_sec;
|
||||
__le32 tv_nsec;
|
||||
};
|
||||
|
||||
/*
|
||||
* handy
|
||||
*/
|
||||
|
||||
#define LEFTMOSTONE 0x80000000
|
||||
#define HIGHORDER 0x80000000u /* high order bit on */
|
||||
#define ONES 0xffffffffu /* all bit on */
|
||||
|
||||
/*
|
||||
* physical xd (pxd)
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned len:24;
|
||||
unsigned addr1:8;
|
||||
__le32 addr2;
|
||||
} pxd_t;
|
||||
|
||||
/* xd_t field construction */
|
||||
|
||||
#define PXDlength(pxd, length32) ((pxd)->len = __cpu_to_le24(length32))
|
||||
#define PXDaddress(pxd, address64)\
|
||||
{\
|
||||
(pxd)->addr1 = ((s64)address64) >> 32;\
|
||||
(pxd)->addr2 = __cpu_to_le32((address64) & 0xffffffff);\
|
||||
}
|
||||
|
||||
/* xd_t field extraction */
|
||||
#define lengthPXD(pxd) __le24_to_cpu((pxd)->len)
|
||||
#define addressPXD(pxd)\
|
||||
( ((s64)((pxd)->addr1)) << 32 | __le32_to_cpu((pxd)->addr2))
|
||||
|
||||
#define MAXTREEHEIGHT 8
|
||||
/* pxd list */
|
||||
struct pxdlist {
|
||||
s16 maxnpxd;
|
||||
s16 npxd;
|
||||
pxd_t pxd[MAXTREEHEIGHT];
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* data extent descriptor (dxd)
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned flag:8; /* 1: flags */
|
||||
unsigned rsrvd:24;
|
||||
__le32 size; /* 4: size in byte */
|
||||
unsigned len:24; /* 3: length in unit of fsblksize */
|
||||
unsigned addr1:8; /* 1: address in unit of fsblksize */
|
||||
__le32 addr2; /* 4: address in unit of fsblksize */
|
||||
} dxd_t; /* - 16 - */
|
||||
|
||||
/* dxd_t flags */
|
||||
#define DXD_INDEX 0x80 /* B+-tree index */
|
||||
#define DXD_INLINE 0x40 /* in-line data extent */
|
||||
#define DXD_EXTENT 0x20 /* out-of-line single extent */
|
||||
#define DXD_FILE 0x10 /* out-of-line file (inode) */
|
||||
#define DXD_CORRUPT 0x08 /* Inconsistency detected */
|
||||
|
||||
/* dxd_t field construction
|
||||
* Conveniently, the PXD macros work for DXD
|
||||
*/
|
||||
#define DXDlength PXDlength
|
||||
#define DXDaddress PXDaddress
|
||||
#define lengthDXD lengthPXD
|
||||
#define addressDXD addressPXD
|
||||
#define DXDsize(dxd, size32) ((dxd)->size = cpu_to_le32(size32))
|
||||
#define sizeDXD(dxd) le32_to_cpu((dxd)->size)
|
||||
|
||||
/*
|
||||
* directory entry argument
|
||||
*/
|
||||
struct component_name {
|
||||
int namlen;
|
||||
wchar_t *name;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* DASD limit information - stored in directory inode
|
||||
*/
|
||||
struct dasd {
|
||||
u8 thresh; /* Alert Threshold (in percent) */
|
||||
u8 delta; /* Alert Threshold delta (in percent) */
|
||||
u8 rsrvd1;
|
||||
u8 limit_hi; /* DASD limit (in logical blocks) */
|
||||
__le32 limit_lo; /* DASD limit (in logical blocks) */
|
||||
u8 rsrvd2[3];
|
||||
u8 used_hi; /* DASD usage (in logical blocks) */
|
||||
__le32 used_lo; /* DASD usage (in logical blocks) */
|
||||
};
|
||||
|
||||
#define DASDLIMIT(dasdp) \
|
||||
(((u64)((dasdp)->limit_hi) << 32) + __le32_to_cpu((dasdp)->limit_lo))
|
||||
#define setDASDLIMIT(dasdp, limit)\
|
||||
{\
|
||||
(dasdp)->limit_hi = ((u64)limit) >> 32;\
|
||||
(dasdp)->limit_lo = __cpu_to_le32(limit);\
|
||||
}
|
||||
#define DASDUSED(dasdp) \
|
||||
(((u64)((dasdp)->used_hi) << 32) + __le32_to_cpu((dasdp)->used_lo))
|
||||
#define setDASDUSED(dasdp, used)\
|
||||
{\
|
||||
(dasdp)->used_hi = ((u64)used) >> 32;\
|
||||
(dasdp)->used_lo = __cpu_to_le32(used);\
|
||||
}
|
||||
|
||||
#endif /* !_H_JFS_TYPES */
|
168
fs/jfs/jfs_umount.c
Normal file
168
fs/jfs/jfs_umount.c
Normal file
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2004
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* jfs_umount.c
|
||||
*
|
||||
* note: file system in transition to aggregate/fileset:
|
||||
* (ref. jfs_mount.c)
|
||||
*
|
||||
* file system unmount is interpreted as mount of the single/only
|
||||
* fileset in the aggregate and, if unmount of the last fileset,
|
||||
* as unmount of the aggerate;
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include "jfs_incore.h"
|
||||
#include "jfs_filsys.h"
|
||||
#include "jfs_superblock.h"
|
||||
#include "jfs_dmap.h"
|
||||
#include "jfs_imap.h"
|
||||
#include "jfs_metapage.h"
|
||||
#include "jfs_debug.h"
|
||||
|
||||
/*
|
||||
* NAME: jfs_umount(vfsp, flags, crp)
|
||||
*
|
||||
* FUNCTION: vfs_umount()
|
||||
*
|
||||
* PARAMETERS: vfsp - virtual file system pointer
|
||||
* flags - unmount for shutdown
|
||||
* crp - credential
|
||||
*
|
||||
* RETURN : EBUSY - device has open files
|
||||
*/
|
||||
int jfs_umount(struct super_block *sb)
|
||||
{
|
||||
struct jfs_sb_info *sbi = JFS_SBI(sb);
|
||||
struct inode *ipbmap = sbi->ipbmap;
|
||||
struct inode *ipimap = sbi->ipimap;
|
||||
struct inode *ipaimap = sbi->ipaimap;
|
||||
struct inode *ipaimap2 = sbi->ipaimap2;
|
||||
struct jfs_log *log;
|
||||
int rc = 0;
|
||||
|
||||
jfs_info("UnMount JFS: sb:0x%p", sb);
|
||||
|
||||
/*
|
||||
* update superblock and close log
|
||||
*
|
||||
* if mounted read-write and log based recovery was enabled
|
||||
*/
|
||||
if ((log = sbi->log))
|
||||
/*
|
||||
* Wait for outstanding transactions to be written to log:
|
||||
*/
|
||||
jfs_flush_journal(log, 2);
|
||||
|
||||
/*
|
||||
* close fileset inode allocation map (aka fileset inode)
|
||||
*/
|
||||
diUnmount(ipimap, 0);
|
||||
|
||||
diFreeSpecial(ipimap);
|
||||
sbi->ipimap = NULL;
|
||||
|
||||
/*
|
||||
* close secondary aggregate inode allocation map
|
||||
*/
|
||||
ipaimap2 = sbi->ipaimap2;
|
||||
if (ipaimap2) {
|
||||
diUnmount(ipaimap2, 0);
|
||||
diFreeSpecial(ipaimap2);
|
||||
sbi->ipaimap2 = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* close aggregate inode allocation map
|
||||
*/
|
||||
ipaimap = sbi->ipaimap;
|
||||
diUnmount(ipaimap, 0);
|
||||
diFreeSpecial(ipaimap);
|
||||
sbi->ipaimap = NULL;
|
||||
|
||||
/*
|
||||
* close aggregate block allocation map
|
||||
*/
|
||||
dbUnmount(ipbmap, 0);
|
||||
|
||||
diFreeSpecial(ipbmap);
|
||||
sbi->ipimap = NULL;
|
||||
|
||||
/*
|
||||
* Make sure all metadata makes it to disk before we mark
|
||||
* the superblock as clean
|
||||
*/
|
||||
filemap_write_and_wait(sbi->direct_inode->i_mapping);
|
||||
|
||||
/*
|
||||
* ensure all file system file pages are propagated to their
|
||||
* home blocks on disk (and their in-memory buffer pages are
|
||||
* invalidated) BEFORE updating file system superblock state
|
||||
* (to signify file system is unmounted cleanly, and thus in
|
||||
* consistent state) and log superblock active file system
|
||||
* list (to signify skip logredo()).
|
||||
*/
|
||||
if (log) { /* log = NULL if read-only mount */
|
||||
updateSuper(sb, FM_CLEAN);
|
||||
|
||||
/*
|
||||
* close log:
|
||||
*
|
||||
* remove file system from log active file system list.
|
||||
*/
|
||||
rc = lmLogClose(sb);
|
||||
}
|
||||
jfs_info("UnMount JFS Complete: rc = %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int jfs_umount_rw(struct super_block *sb)
|
||||
{
|
||||
struct jfs_sb_info *sbi = JFS_SBI(sb);
|
||||
struct jfs_log *log = sbi->log;
|
||||
|
||||
if (!log)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* close log:
|
||||
*
|
||||
* remove file system from log active file system list.
|
||||
*/
|
||||
jfs_flush_journal(log, 2);
|
||||
|
||||
/*
|
||||
* Make sure all metadata makes it to disk
|
||||
*/
|
||||
dbSync(sbi->ipbmap);
|
||||
diSync(sbi->ipimap);
|
||||
|
||||
/*
|
||||
* Note that we have to do this even if sync_blockdev() will
|
||||
* do exactly the same a few instructions later: We can't
|
||||
* mark the superblock clean before everything is flushed to
|
||||
* disk.
|
||||
*/
|
||||
filemap_write_and_wait(sbi->direct_inode->i_mapping);
|
||||
|
||||
updateSuper(sb, FM_CLEAN);
|
||||
|
||||
return lmLogClose(sb);
|
||||
}
|
138
fs/jfs/jfs_unicode.c
Normal file
138
fs/jfs/jfs_unicode.c
Normal file
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2004
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include "jfs_incore.h"
|
||||
#include "jfs_filsys.h"
|
||||
#include "jfs_unicode.h"
|
||||
#include "jfs_debug.h"
|
||||
|
||||
/*
|
||||
* NAME: jfs_strfromUCS()
|
||||
*
|
||||
* FUNCTION: Convert little-endian unicode string to character string
|
||||
*
|
||||
*/
|
||||
int jfs_strfromUCS_le(char *to, const __le16 * from,
|
||||
int len, struct nls_table *codepage)
|
||||
{
|
||||
int i;
|
||||
int outlen = 0;
|
||||
static int warn_again = 5; /* Only warn up to 5 times total */
|
||||
int warn = !!warn_again; /* once per string */
|
||||
|
||||
if (codepage) {
|
||||
for (i = 0; (i < len) && from[i]; i++) {
|
||||
int charlen;
|
||||
charlen =
|
||||
codepage->uni2char(le16_to_cpu(from[i]),
|
||||
&to[outlen],
|
||||
NLS_MAX_CHARSET_SIZE);
|
||||
if (charlen > 0)
|
||||
outlen += charlen;
|
||||
else
|
||||
to[outlen++] = '?';
|
||||
}
|
||||
} else {
|
||||
for (i = 0; (i < len) && from[i]; i++) {
|
||||
if (unlikely(le16_to_cpu(from[i]) & 0xff00)) {
|
||||
to[i] = '?';
|
||||
if (unlikely(warn)) {
|
||||
warn--;
|
||||
warn_again--;
|
||||
printk(KERN_ERR
|
||||
"non-latin1 character 0x%x found in JFS file name\n",
|
||||
le16_to_cpu(from[i]));
|
||||
printk(KERN_ERR
|
||||
"mount with iocharset=utf8 to access\n");
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
to[i] = (char) (le16_to_cpu(from[i]));
|
||||
}
|
||||
outlen = i;
|
||||
}
|
||||
to[outlen] = 0;
|
||||
return outlen;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: jfs_strtoUCS()
|
||||
*
|
||||
* FUNCTION: Convert character string to unicode string
|
||||
*
|
||||
*/
|
||||
static int jfs_strtoUCS(wchar_t * to, const unsigned char *from, int len,
|
||||
struct nls_table *codepage)
|
||||
{
|
||||
int charlen;
|
||||
int i;
|
||||
|
||||
if (codepage) {
|
||||
for (i = 0; len && *from; i++, from += charlen, len -= charlen)
|
||||
{
|
||||
charlen = codepage->char2uni(from, len, &to[i]);
|
||||
if (charlen < 1) {
|
||||
jfs_err("jfs_strtoUCS: char2uni returned %d.",
|
||||
charlen);
|
||||
jfs_err("charset = %s, char = 0x%x",
|
||||
codepage->charset, *from);
|
||||
return charlen;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 0; (i < len) && from[i]; i++)
|
||||
to[i] = (wchar_t) from[i];
|
||||
}
|
||||
|
||||
to[i] = 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: get_UCSname()
|
||||
*
|
||||
* FUNCTION: Allocate and translate to unicode string
|
||||
*
|
||||
*/
|
||||
int get_UCSname(struct component_name * uniName, struct dentry *dentry)
|
||||
{
|
||||
struct nls_table *nls_tab = JFS_SBI(dentry->d_sb)->nls_tab;
|
||||
int length = dentry->d_name.len;
|
||||
|
||||
if (length > JFS_NAME_MAX)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
uniName->name =
|
||||
kmalloc((length + 1) * sizeof(wchar_t), GFP_NOFS);
|
||||
|
||||
if (uniName->name == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
uniName->namlen = jfs_strtoUCS(uniName->name, dentry->d_name.name,
|
||||
length, nls_tab);
|
||||
|
||||
if (uniName->namlen < 0) {
|
||||
kfree(uniName->name);
|
||||
return uniName->namlen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
156
fs/jfs/jfs_unicode.h
Normal file
156
fs/jfs/jfs_unicode.h
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2002
|
||||
* Portions Copyright (C) Christoph Hellwig, 2001-2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_UNICODE
|
||||
#define _H_JFS_UNICODE
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include "jfs_types.h"
|
||||
|
||||
typedef struct {
|
||||
wchar_t start;
|
||||
wchar_t end;
|
||||
signed char *table;
|
||||
} UNICASERANGE;
|
||||
|
||||
extern signed char UniUpperTable[512];
|
||||
extern UNICASERANGE UniUpperRange[];
|
||||
extern int get_UCSname(struct component_name *, struct dentry *);
|
||||
extern int jfs_strfromUCS_le(char *, const __le16 *, int, struct nls_table *);
|
||||
|
||||
#define free_UCSname(COMP) kfree((COMP)->name)
|
||||
|
||||
/*
|
||||
* UniStrcpy: Copy a string
|
||||
*/
|
||||
static inline wchar_t *UniStrcpy(wchar_t * ucs1, const wchar_t * ucs2)
|
||||
{
|
||||
wchar_t *anchor = ucs1; /* save the start of result string */
|
||||
|
||||
while ((*ucs1++ = *ucs2++));
|
||||
return anchor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* UniStrncpy: Copy length limited string with pad
|
||||
*/
|
||||
static inline __le16 *UniStrncpy_le(__le16 * ucs1, const __le16 * ucs2,
|
||||
size_t n)
|
||||
{
|
||||
__le16 *anchor = ucs1;
|
||||
|
||||
while (n-- && *ucs2) /* Copy the strings */
|
||||
*ucs1++ = *ucs2++;
|
||||
|
||||
n++;
|
||||
while (n--) /* Pad with nulls */
|
||||
*ucs1++ = 0;
|
||||
return anchor;
|
||||
}
|
||||
|
||||
/*
|
||||
* UniStrncmp_le: Compare length limited string - native to little-endian
|
||||
*/
|
||||
static inline int UniStrncmp_le(const wchar_t * ucs1, const __le16 * ucs2,
|
||||
size_t n)
|
||||
{
|
||||
if (!n)
|
||||
return 0; /* Null strings are equal */
|
||||
while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) {
|
||||
ucs1++;
|
||||
ucs2++;
|
||||
}
|
||||
return (int) *ucs1 - (int) __le16_to_cpu(*ucs2);
|
||||
}
|
||||
|
||||
/*
|
||||
* UniStrncpy_to_le: Copy length limited string with pad to little-endian
|
||||
*/
|
||||
static inline __le16 *UniStrncpy_to_le(__le16 * ucs1, const wchar_t * ucs2,
|
||||
size_t n)
|
||||
{
|
||||
__le16 *anchor = ucs1;
|
||||
|
||||
while (n-- && *ucs2) /* Copy the strings */
|
||||
*ucs1++ = cpu_to_le16(*ucs2++);
|
||||
|
||||
n++;
|
||||
while (n--) /* Pad with nulls */
|
||||
*ucs1++ = 0;
|
||||
return anchor;
|
||||
}
|
||||
|
||||
/*
|
||||
* UniStrncpy_from_le: Copy length limited string with pad from little-endian
|
||||
*/
|
||||
static inline wchar_t *UniStrncpy_from_le(wchar_t * ucs1, const __le16 * ucs2,
|
||||
size_t n)
|
||||
{
|
||||
wchar_t *anchor = ucs1;
|
||||
|
||||
while (n-- && *ucs2) /* Copy the strings */
|
||||
*ucs1++ = __le16_to_cpu(*ucs2++);
|
||||
|
||||
n++;
|
||||
while (n--) /* Pad with nulls */
|
||||
*ucs1++ = 0;
|
||||
return anchor;
|
||||
}
|
||||
|
||||
/*
|
||||
* UniToupper: Convert a unicode character to upper case
|
||||
*/
|
||||
static inline wchar_t UniToupper(wchar_t uc)
|
||||
{
|
||||
UNICASERANGE *rp;
|
||||
|
||||
if (uc < sizeof(UniUpperTable)) { /* Latin characters */
|
||||
return uc + UniUpperTable[uc]; /* Use base tables */
|
||||
} else {
|
||||
rp = UniUpperRange; /* Use range tables */
|
||||
while (rp->start) {
|
||||
if (uc < rp->start) /* Before start of range */
|
||||
return uc; /* Uppercase = input */
|
||||
if (uc <= rp->end) /* In range */
|
||||
return uc + rp->table[uc - rp->start];
|
||||
rp++; /* Try next range */
|
||||
}
|
||||
}
|
||||
return uc; /* Past last range */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* UniStrupr: Upper case a unicode string
|
||||
*/
|
||||
static inline wchar_t *UniStrupr(wchar_t * upin)
|
||||
{
|
||||
wchar_t *up;
|
||||
|
||||
up = upin;
|
||||
while (*up) { /* For all characters */
|
||||
*up = UniToupper(*up);
|
||||
up++;
|
||||
}
|
||||
return upin; /* Return input pointer */
|
||||
}
|
||||
|
||||
#endif /* !_H_JFS_UNICODE */
|
134
fs/jfs/jfs_uniupr.c
Normal file
134
fs/jfs/jfs_uniupr.c
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include "jfs_unicode.h"
|
||||
|
||||
/*
|
||||
* Latin upper case
|
||||
*/
|
||||
signed char UniUpperTable[512] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */
|
||||
0,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 060-06f */
|
||||
-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, 0, 0, 0, 0, 0, /* 070-07f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */
|
||||
-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 0e0-0ef */
|
||||
-32,-32,-32,-32,-32,-32,-32, 0,-32,-32,-32,-32,-32,-32,-32,121, /* 0f0-0ff */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */
|
||||
0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */
|
||||
-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */
|
||||
0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */
|
||||
0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */
|
||||
0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */
|
||||
-1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */
|
||||
0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */
|
||||
-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1,-79, 0, -1, /* 1d0-1df */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */
|
||||
0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */
|
||||
};
|
||||
|
||||
/* Upper case range - Greek */
|
||||
static signed char UniCaseRangeU03a0[47] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-38,-37,-37,-37, /* 3a0-3af */
|
||||
0,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 3b0-3bf */
|
||||
-32,-32,-31,-32,-32,-32,-32,-32,-32,-32,-32,-32,-64,-63,-63,
|
||||
};
|
||||
|
||||
/* Upper case range - Cyrillic */
|
||||
static signed char UniCaseRangeU0430[48] = {
|
||||
-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 430-43f */
|
||||
-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 440-44f */
|
||||
0,-80,-80,-80,-80,-80,-80,-80,-80,-80,-80,-80,-80, 0,-80,-80, /* 450-45f */
|
||||
};
|
||||
|
||||
/* Upper case range - Extended cyrillic */
|
||||
static signed char UniCaseRangeU0490[61] = {
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */
|
||||
0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1,
|
||||
};
|
||||
|
||||
/* Upper case range - Extended latin and greek */
|
||||
static signed char UniCaseRangeU1e00[509] = {
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */
|
||||
0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0,-59, 0, -1, 0, -1, /* 1e90-1e9f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */
|
||||
8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */
|
||||
8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */
|
||||
0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */
|
||||
74, 74, 86, 86, 86, 86,100,100, 0, 0,112,112,126,126, 0, 0, /* 1f70-1f7f */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */
|
||||
8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */
|
||||
0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */
|
||||
8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */
|
||||
8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */
|
||||
0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
/* Upper case range - Wide latin */
|
||||
static signed char UniCaseRangeUff40[27] = {
|
||||
0,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* ff40-ff4f */
|
||||
-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,
|
||||
};
|
||||
|
||||
/*
|
||||
* Upper Case Range
|
||||
*/
|
||||
UNICASERANGE UniUpperRange[] = {
|
||||
{ 0x03a0, 0x03ce, UniCaseRangeU03a0 },
|
||||
{ 0x0430, 0x045f, UniCaseRangeU0430 },
|
||||
{ 0x0490, 0x04cc, UniCaseRangeU0490 },
|
||||
{ 0x1e00, 0x1ffc, UniCaseRangeU1e00 },
|
||||
{ 0xff40, 0xff5a, UniCaseRangeUff40 },
|
||||
{ 0 }
|
||||
};
|
77
fs/jfs/jfs_xattr.h
Normal file
77
fs/jfs/jfs_xattr.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef H_JFS_XATTR
|
||||
#define H_JFS_XATTR
|
||||
|
||||
/*
|
||||
* jfs_ea_list describe the on-disk format of the extended attributes.
|
||||
* I know the null-terminator is redundant since namelen is stored, but
|
||||
* I am maintaining compatibility with OS/2 where possible.
|
||||
*/
|
||||
struct jfs_ea {
|
||||
u8 flag; /* Unused? */
|
||||
u8 namelen; /* Length of name */
|
||||
__le16 valuelen; /* Length of value */
|
||||
char name[0]; /* Attribute name (includes null-terminator) */
|
||||
}; /* Value immediately follows name */
|
||||
|
||||
struct jfs_ea_list {
|
||||
__le32 size; /* overall size */
|
||||
struct jfs_ea ea[0]; /* Variable length list */
|
||||
};
|
||||
|
||||
/* Macros for defining maxiumum number of bytes supported for EAs */
|
||||
#define MAXEASIZE 65535
|
||||
#define MAXEALISTSIZE MAXEASIZE
|
||||
|
||||
/*
|
||||
* some macros for dealing with variable length EA lists.
|
||||
*/
|
||||
#define EA_SIZE(ea) \
|
||||
(sizeof (struct jfs_ea) + (ea)->namelen + 1 + \
|
||||
le16_to_cpu((ea)->valuelen))
|
||||
#define NEXT_EA(ea) ((struct jfs_ea *) (((char *) (ea)) + (EA_SIZE (ea))))
|
||||
#define FIRST_EA(ealist) ((ealist)->ea)
|
||||
#define EALIST_SIZE(ealist) le32_to_cpu((ealist)->size)
|
||||
#define END_EALIST(ealist) \
|
||||
((struct jfs_ea *) (((char *) (ealist)) + EALIST_SIZE(ealist)))
|
||||
|
||||
extern int __jfs_setxattr(tid_t, struct inode *, const char *, const void *,
|
||||
size_t, int);
|
||||
extern int jfs_setxattr(struct dentry *, const char *, const void *, size_t,
|
||||
int);
|
||||
extern ssize_t __jfs_getxattr(struct inode *, const char *, void *, size_t);
|
||||
extern ssize_t jfs_getxattr(struct dentry *, const char *, void *, size_t);
|
||||
extern ssize_t jfs_listxattr(struct dentry *, char *, size_t);
|
||||
extern int jfs_removexattr(struct dentry *, const char *);
|
||||
|
||||
extern const struct xattr_handler *jfs_xattr_handlers[];
|
||||
|
||||
#ifdef CONFIG_JFS_SECURITY
|
||||
extern int jfs_init_security(tid_t, struct inode *, struct inode *,
|
||||
const struct qstr *);
|
||||
#else
|
||||
static inline int jfs_init_security(tid_t tid, struct inode *inode,
|
||||
struct inode *dir, const struct qstr *qstr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* H_JFS_XATTR */
|
3903
fs/jfs/jfs_xtree.c
Normal file
3903
fs/jfs/jfs_xtree.c
Normal file
File diff suppressed because it is too large
Load diff
132
fs/jfs/jfs_xtree.h
Normal file
132
fs/jfs/jfs_xtree.h
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _H_JFS_XTREE
|
||||
#define _H_JFS_XTREE
|
||||
|
||||
/*
|
||||
* jfs_xtree.h: extent allocation descriptor B+-tree manager
|
||||
*/
|
||||
|
||||
#include "jfs_btree.h"
|
||||
|
||||
|
||||
/*
|
||||
* extent allocation descriptor (xad)
|
||||
*/
|
||||
typedef struct xad {
|
||||
unsigned flag:8; /* 1: flag */
|
||||
unsigned rsvrd:16; /* 2: reserved */
|
||||
unsigned off1:8; /* 1: offset in unit of fsblksize */
|
||||
__le32 off2; /* 4: offset in unit of fsblksize */
|
||||
unsigned len:24; /* 3: length in unit of fsblksize */
|
||||
unsigned addr1:8; /* 1: address in unit of fsblksize */
|
||||
__le32 addr2; /* 4: address in unit of fsblksize */
|
||||
} xad_t; /* (16) */
|
||||
|
||||
#define MAXXLEN ((1 << 24) - 1)
|
||||
|
||||
#define XTSLOTSIZE 16
|
||||
#define L2XTSLOTSIZE 4
|
||||
|
||||
/* xad_t field construction */
|
||||
#define XADoffset(xad, offset64)\
|
||||
{\
|
||||
(xad)->off1 = ((u64)offset64) >> 32;\
|
||||
(xad)->off2 = __cpu_to_le32((offset64) & 0xffffffff);\
|
||||
}
|
||||
#define XADaddress(xad, address64)\
|
||||
{\
|
||||
(xad)->addr1 = ((u64)address64) >> 32;\
|
||||
(xad)->addr2 = __cpu_to_le32((address64) & 0xffffffff);\
|
||||
}
|
||||
#define XADlength(xad, length32) (xad)->len = __cpu_to_le24(length32)
|
||||
|
||||
/* xad_t field extraction */
|
||||
#define offsetXAD(xad)\
|
||||
( ((s64)((xad)->off1)) << 32 | __le32_to_cpu((xad)->off2))
|
||||
#define addressXAD(xad)\
|
||||
( ((s64)((xad)->addr1)) << 32 | __le32_to_cpu((xad)->addr2))
|
||||
#define lengthXAD(xad) __le24_to_cpu((xad)->len)
|
||||
|
||||
/* xad list */
|
||||
struct xadlist {
|
||||
s16 maxnxad;
|
||||
s16 nxad;
|
||||
xad_t *xad;
|
||||
};
|
||||
|
||||
/* xad_t flags */
|
||||
#define XAD_NEW 0x01 /* new */
|
||||
#define XAD_EXTENDED 0x02 /* extended */
|
||||
#define XAD_COMPRESSED 0x04 /* compressed with recorded length */
|
||||
#define XAD_NOTRECORDED 0x08 /* allocated but not recorded */
|
||||
#define XAD_COW 0x10 /* copy-on-write */
|
||||
|
||||
|
||||
/* possible values for maxentry */
|
||||
#define XTROOTINITSLOT_DIR 6
|
||||
#define XTROOTINITSLOT 10
|
||||
#define XTROOTMAXSLOT 18
|
||||
#define XTPAGEMAXSLOT 256
|
||||
#define XTENTRYSTART 2
|
||||
|
||||
/*
|
||||
* xtree page:
|
||||
*/
|
||||
typedef union {
|
||||
struct xtheader {
|
||||
__le64 next; /* 8: */
|
||||
__le64 prev; /* 8: */
|
||||
|
||||
u8 flag; /* 1: */
|
||||
u8 rsrvd1; /* 1: */
|
||||
__le16 nextindex; /* 2: next index = number of entries */
|
||||
__le16 maxentry; /* 2: max number of entries */
|
||||
__le16 rsrvd2; /* 2: */
|
||||
|
||||
pxd_t self; /* 8: self */
|
||||
} header; /* (32) */
|
||||
|
||||
xad_t xad[XTROOTMAXSLOT]; /* 16 * maxentry: xad array */
|
||||
} xtpage_t;
|
||||
|
||||
/*
|
||||
* external declaration
|
||||
*/
|
||||
extern int xtLookup(struct inode *ip, s64 lstart, s64 llen,
|
||||
int *pflag, s64 * paddr, int *plen, int flag);
|
||||
extern void xtInitRoot(tid_t tid, struct inode *ip);
|
||||
extern int xtInsert(tid_t tid, struct inode *ip,
|
||||
int xflag, s64 xoff, int xlen, s64 * xaddrp, int flag);
|
||||
extern int xtExtend(tid_t tid, struct inode *ip, s64 xoff, int xlen,
|
||||
int flag);
|
||||
#ifdef _NOTYET
|
||||
extern int xtTailgate(tid_t tid, struct inode *ip,
|
||||
s64 xoff, int xlen, s64 xaddr, int flag);
|
||||
#endif
|
||||
extern int xtUpdate(tid_t tid, struct inode *ip, struct xad *nxad);
|
||||
extern int xtDelete(tid_t tid, struct inode *ip, s64 xoff, int xlen,
|
||||
int flag);
|
||||
extern s64 xtTruncate(tid_t tid, struct inode *ip, s64 newsize, int type);
|
||||
extern s64 xtTruncate_pmap(tid_t tid, struct inode *ip, s64 committed_size);
|
||||
extern int xtRelocate(tid_t tid, struct inode *ip,
|
||||
xad_t * oxad, s64 nxaddr, int xtype);
|
||||
extern int xtAppend(tid_t tid,
|
||||
struct inode *ip, int xflag, s64 xoff, int maxblocks,
|
||||
int *xlenp, s64 * xaddrp, int flag);
|
||||
#endif /* !_H_JFS_XTREE */
|
1608
fs/jfs/namei.c
Normal file
1608
fs/jfs/namei.c
Normal file
File diff suppressed because it is too large
Load diff
543
fs/jfs/resize.c
Normal file
543
fs/jfs/resize.c
Normal file
|
@ -0,0 +1,543 @@
|
|||
/*
|
||||
* Copyright (C) International Business Machines Corp., 2000-2004
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/quotaops.h>
|
||||
#include "jfs_incore.h"
|
||||
#include "jfs_filsys.h"
|
||||
#include "jfs_metapage.h"
|
||||
#include "jfs_dinode.h"
|
||||
#include "jfs_imap.h"
|
||||
#include "jfs_dmap.h"
|
||||
#include "jfs_superblock.h"
|
||||
#include "jfs_txnmgr.h"
|
||||
#include "jfs_debug.h"
|
||||
|
||||
#define BITSPERPAGE (PSIZE << 3)
|
||||
#define L2MEGABYTE 20
|
||||
#define MEGABYTE (1 << L2MEGABYTE)
|
||||
#define MEGABYTE32 (MEGABYTE << 5)
|
||||
|
||||
/* convert block number to bmap file page number */
|
||||
#define BLKTODMAPN(b)\
|
||||
(((b) >> 13) + ((b) >> 23) + ((b) >> 33) + 3 + 1)
|
||||
|
||||
/*
|
||||
* jfs_extendfs()
|
||||
*
|
||||
* function: extend file system;
|
||||
*
|
||||
* |-------------------------------|----------|----------|
|
||||
* file system space fsck inline log
|
||||
* workspace space
|
||||
*
|
||||
* input:
|
||||
* new LVSize: in LV blocks (required)
|
||||
* new LogSize: in LV blocks (optional)
|
||||
* new FSSize: in LV blocks (optional)
|
||||
*
|
||||
* new configuration:
|
||||
* 1. set new LogSize as specified or default from new LVSize;
|
||||
* 2. compute new FSCKSize from new LVSize;
|
||||
* 3. set new FSSize as MIN(FSSize, LVSize-(LogSize+FSCKSize)) where
|
||||
* assert(new FSSize >= old FSSize),
|
||||
* i.e., file system must not be shrunk;
|
||||
*/
|
||||
int jfs_extendfs(struct super_block *sb, s64 newLVSize, int newLogSize)
|
||||
{
|
||||
int rc = 0;
|
||||
struct jfs_sb_info *sbi = JFS_SBI(sb);
|
||||
struct inode *ipbmap = sbi->ipbmap;
|
||||
struct inode *ipbmap2;
|
||||
struct inode *ipimap = sbi->ipimap;
|
||||
struct jfs_log *log = sbi->log;
|
||||
struct bmap *bmp = sbi->bmap;
|
||||
s64 newLogAddress, newFSCKAddress;
|
||||
int newFSCKSize;
|
||||
s64 newMapSize = 0, mapSize;
|
||||
s64 XAddress, XSize, nblocks, xoff, xaddr, t64;
|
||||
s64 oldLVSize;
|
||||
s64 newFSSize;
|
||||
s64 VolumeSize;
|
||||
int newNpages = 0, nPages, newPage, xlen, t32;
|
||||
int tid;
|
||||
int log_formatted = 0;
|
||||
struct inode *iplist[1];
|
||||
struct jfs_superblock *j_sb, *j_sb2;
|
||||
s64 old_agsize;
|
||||
int agsizechanged = 0;
|
||||
struct buffer_head *bh, *bh2;
|
||||
|
||||
/* If the volume hasn't grown, get out now */
|
||||
|
||||
if (sbi->mntflag & JFS_INLINELOG)
|
||||
oldLVSize = addressPXD(&sbi->logpxd) + lengthPXD(&sbi->logpxd);
|
||||
else
|
||||
oldLVSize = addressPXD(&sbi->fsckpxd) +
|
||||
lengthPXD(&sbi->fsckpxd);
|
||||
|
||||
if (oldLVSize >= newLVSize) {
|
||||
printk(KERN_WARNING
|
||||
"jfs_extendfs: volume hasn't grown, returning\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
VolumeSize = sb->s_bdev->bd_inode->i_size >> sb->s_blocksize_bits;
|
||||
|
||||
if (VolumeSize) {
|
||||
if (newLVSize > VolumeSize) {
|
||||
printk(KERN_WARNING "jfs_extendfs: invalid size\n");
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
/* check the device */
|
||||
bh = sb_bread(sb, newLVSize - 1);
|
||||
if (!bh) {
|
||||
printk(KERN_WARNING "jfs_extendfs: invalid size\n");
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
bforget(bh);
|
||||
}
|
||||
|
||||
/* Can't extend write-protected drive */
|
||||
|
||||
if (isReadOnly(ipbmap)) {
|
||||
printk(KERN_WARNING "jfs_extendfs: read-only file system\n");
|
||||
rc = -EROFS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* reconfigure LV spaces
|
||||
* ---------------------
|
||||
*
|
||||
* validate new size, or, if not specified, determine new size
|
||||
*/
|
||||
|
||||
/*
|
||||
* reconfigure inline log space:
|
||||
*/
|
||||
if ((sbi->mntflag & JFS_INLINELOG)) {
|
||||
if (newLogSize == 0) {
|
||||
/*
|
||||
* no size specified: default to 1/256 of aggregate
|
||||
* size; rounded up to a megabyte boundary;
|
||||
*/
|
||||
newLogSize = newLVSize >> 8;
|
||||
t32 = (1 << (20 - sbi->l2bsize)) - 1;
|
||||
newLogSize = (newLogSize + t32) & ~t32;
|
||||
newLogSize =
|
||||
min(newLogSize, MEGABYTE32 >> sbi->l2bsize);
|
||||
} else {
|
||||
/*
|
||||
* convert the newLogSize to fs blocks.
|
||||
*
|
||||
* Since this is given in megabytes, it will always be
|
||||
* an even number of pages.
|
||||
*/
|
||||
newLogSize = (newLogSize * MEGABYTE) >> sbi->l2bsize;
|
||||
}
|
||||
|
||||
} else
|
||||
newLogSize = 0;
|
||||
|
||||
newLogAddress = newLVSize - newLogSize;
|
||||
|
||||
/*
|
||||
* reconfigure fsck work space:
|
||||
*
|
||||
* configure it to the end of the logical volume regardless of
|
||||
* whether file system extends to the end of the aggregate;
|
||||
* Need enough 4k pages to cover:
|
||||
* - 1 bit per block in aggregate rounded up to BPERDMAP boundary
|
||||
* - 1 extra page to handle control page and intermediate level pages
|
||||
* - 50 extra pages for the chkdsk service log
|
||||
*/
|
||||
t64 = ((newLVSize - newLogSize + BPERDMAP - 1) >> L2BPERDMAP)
|
||||
<< L2BPERDMAP;
|
||||
t32 = DIV_ROUND_UP(t64, BITSPERPAGE) + 1 + 50;
|
||||
newFSCKSize = t32 << sbi->l2nbperpage;
|
||||
newFSCKAddress = newLogAddress - newFSCKSize;
|
||||
|
||||
/*
|
||||
* compute new file system space;
|
||||
*/
|
||||
newFSSize = newLVSize - newLogSize - newFSCKSize;
|
||||
|
||||
/* file system cannot be shrunk */
|
||||
if (newFSSize < bmp->db_mapsize) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're expanding enough that the inline log does not overlap
|
||||
* the old one, we can format the new log before we quiesce the
|
||||
* filesystem.
|
||||
*/
|
||||
if ((sbi->mntflag & JFS_INLINELOG) && (newLogAddress > oldLVSize)) {
|
||||
if ((rc = lmLogFormat(log, newLogAddress, newLogSize)))
|
||||
goto out;
|
||||
log_formatted = 1;
|
||||
}
|
||||
/*
|
||||
* quiesce file system
|
||||
*
|
||||
* (prepare to move the inline log and to prevent map update)
|
||||
*
|
||||
* block any new transactions and wait for completion of
|
||||
* all wip transactions and flush modified pages s.t.
|
||||
* on-disk file system is in consistent state and
|
||||
* log is not required for recovery.
|
||||
*/
|
||||
txQuiesce(sb);
|
||||
|
||||
/* Reset size of direct inode */
|
||||
sbi->direct_inode->i_size = sb->s_bdev->bd_inode->i_size;
|
||||
|
||||
if (sbi->mntflag & JFS_INLINELOG) {
|
||||
/*
|
||||
* deactivate old inline log
|
||||
*/
|
||||
lmLogShutdown(log);
|
||||
|
||||
/*
|
||||
* mark on-disk super block for fs in transition;
|
||||
*
|
||||
* update on-disk superblock for the new space configuration
|
||||
* of inline log space and fsck work space descriptors:
|
||||
* N.B. FS descriptor is NOT updated;
|
||||
*
|
||||
* crash recovery:
|
||||
* logredo(): if FM_EXTENDFS, return to fsck() for cleanup;
|
||||
* fsck(): if FM_EXTENDFS, reformat inline log and fsck
|
||||
* workspace from superblock inline log descriptor and fsck
|
||||
* workspace descriptor;
|
||||
*/
|
||||
|
||||
/* read in superblock */
|
||||
if ((rc = readSuper(sb, &bh)))
|
||||
goto error_out;
|
||||
j_sb = (struct jfs_superblock *)bh->b_data;
|
||||
|
||||
/* mark extendfs() in progress */
|
||||
j_sb->s_state |= cpu_to_le32(FM_EXTENDFS);
|
||||
j_sb->s_xsize = cpu_to_le64(newFSSize);
|
||||
PXDaddress(&j_sb->s_xfsckpxd, newFSCKAddress);
|
||||
PXDlength(&j_sb->s_xfsckpxd, newFSCKSize);
|
||||
PXDaddress(&j_sb->s_xlogpxd, newLogAddress);
|
||||
PXDlength(&j_sb->s_xlogpxd, newLogSize);
|
||||
|
||||
/* synchronously update superblock */
|
||||
mark_buffer_dirty(bh);
|
||||
sync_dirty_buffer(bh);
|
||||
brelse(bh);
|
||||
|
||||
/*
|
||||
* format new inline log synchronously;
|
||||
*
|
||||
* crash recovery: if log move in progress,
|
||||
* reformat log and exit success;
|
||||
*/
|
||||
if (!log_formatted)
|
||||
if ((rc = lmLogFormat(log, newLogAddress, newLogSize)))
|
||||
goto error_out;
|
||||
|
||||
/*
|
||||
* activate new log
|
||||
*/
|
||||
log->base = newLogAddress;
|
||||
log->size = newLogSize >> (L2LOGPSIZE - sb->s_blocksize_bits);
|
||||
if ((rc = lmLogInit(log)))
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* extend block allocation map
|
||||
* ---------------------------
|
||||
*
|
||||
* extendfs() for new extension, retry after crash recovery;
|
||||
*
|
||||
* note: both logredo() and fsck() rebuild map from
|
||||
* the bitmap and configuration parameter from superblock
|
||||
* (disregarding all other control information in the map);
|
||||
*
|
||||
* superblock:
|
||||
* s_size: aggregate size in physical blocks;
|
||||
*/
|
||||
/*
|
||||
* compute the new block allocation map configuration
|
||||
*
|
||||
* map dinode:
|
||||
* di_size: map file size in byte;
|
||||
* di_nblocks: number of blocks allocated for map file;
|
||||
* di_mapsize: number of blocks in aggregate (covered by map);
|
||||
* map control page:
|
||||
* db_mapsize: number of blocks in aggregate (covered by map);
|
||||
*/
|
||||
newMapSize = newFSSize;
|
||||
/* number of data pages of new bmap file:
|
||||
* roundup new size to full dmap page boundary and
|
||||
* add 1 extra dmap page for next extendfs()
|
||||
*/
|
||||
t64 = (newMapSize - 1) + BPERDMAP;
|
||||
newNpages = BLKTODMAPN(t64) + 1;
|
||||
|
||||
/*
|
||||
* extend map from current map (WITHOUT growing mapfile)
|
||||
*
|
||||
* map new extension with unmapped part of the last partial
|
||||
* dmap page, if applicable, and extra page(s) allocated
|
||||
* at end of bmap by mkfs() or previous extendfs();
|
||||
*/
|
||||
extendBmap:
|
||||
/* compute number of blocks requested to extend */
|
||||
mapSize = bmp->db_mapsize;
|
||||
XAddress = mapSize; /* eXtension Address */
|
||||
XSize = newMapSize - mapSize; /* eXtension Size */
|
||||
old_agsize = bmp->db_agsize; /* We need to know if this changes */
|
||||
|
||||
/* compute number of blocks that can be extended by current mapfile */
|
||||
t64 = dbMapFileSizeToMapSize(ipbmap);
|
||||
if (mapSize > t64) {
|
||||
printk(KERN_ERR "jfs_extendfs: mapSize (0x%Lx) > t64 (0x%Lx)\n",
|
||||
(long long) mapSize, (long long) t64);
|
||||
rc = -EIO;
|
||||
goto error_out;
|
||||
}
|
||||
nblocks = min(t64 - mapSize, XSize);
|
||||
|
||||
/*
|
||||
* update map pages for new extension:
|
||||
*
|
||||
* update/init dmap and bubble up the control hierarchy
|
||||
* incrementally fold up dmaps into upper levels;
|
||||
* update bmap control page;
|
||||
*/
|
||||
if ((rc = dbExtendFS(ipbmap, XAddress, nblocks)))
|
||||
goto error_out;
|
||||
|
||||
agsizechanged |= (bmp->db_agsize != old_agsize);
|
||||
|
||||
/*
|
||||
* the map now has extended to cover additional nblocks:
|
||||
* dn_mapsize = oldMapsize + nblocks;
|
||||
*/
|
||||
/* ipbmap->i_mapsize += nblocks; */
|
||||
XSize -= nblocks;
|
||||
|
||||
/*
|
||||
* grow map file to cover remaining extension
|
||||
* and/or one extra dmap page for next extendfs();
|
||||
*
|
||||
* allocate new map pages and its backing blocks, and
|
||||
* update map file xtree
|
||||
*/
|
||||
/* compute number of data pages of current bmap file */
|
||||
nPages = ipbmap->i_size >> L2PSIZE;
|
||||
|
||||
/* need to grow map file ? */
|
||||
if (nPages == newNpages)
|
||||
goto finalizeBmap;
|
||||
|
||||
/*
|
||||
* grow bmap file for the new map pages required:
|
||||
*
|
||||
* allocate growth at the start of newly extended region;
|
||||
* bmap file only grows sequentially, i.e., both data pages
|
||||
* and possibly xtree index pages may grow in append mode,
|
||||
* s.t. logredo() can reconstruct pre-extension state
|
||||
* by washing away bmap file of pages outside s_size boundary;
|
||||
*/
|
||||
/*
|
||||
* journal map file growth as if a regular file growth:
|
||||
* (note: bmap is created with di_mode = IFJOURNAL|IFREG);
|
||||
*
|
||||
* journaling of bmap file growth is not required since
|
||||
* logredo() do/can not use log records of bmap file growth
|
||||
* but it provides careful write semantics, pmap update, etc.;
|
||||
*/
|
||||
/* synchronous write of data pages: bmap data pages are
|
||||
* cached in meta-data cache, and not written out
|
||||
* by txCommit();
|
||||
*/
|
||||
filemap_fdatawait(ipbmap->i_mapping);
|
||||
filemap_write_and_wait(ipbmap->i_mapping);
|
||||
diWriteSpecial(ipbmap, 0);
|
||||
|
||||
newPage = nPages; /* first new page number */
|
||||
xoff = newPage << sbi->l2nbperpage;
|
||||
xlen = (newNpages - nPages) << sbi->l2nbperpage;
|
||||
xlen = min(xlen, (int) nblocks) & ~(sbi->nbperpage - 1);
|
||||
xaddr = XAddress;
|
||||
|
||||
tid = txBegin(sb, COMMIT_FORCE);
|
||||
|
||||
if ((rc = xtAppend(tid, ipbmap, 0, xoff, nblocks, &xlen, &xaddr, 0))) {
|
||||
txEnd(tid);
|
||||
goto error_out;
|
||||
}
|
||||
/* update bmap file size */
|
||||
ipbmap->i_size += xlen << sbi->l2bsize;
|
||||
inode_add_bytes(ipbmap, xlen << sbi->l2bsize);
|
||||
|
||||
iplist[0] = ipbmap;
|
||||
rc = txCommit(tid, 1, &iplist[0], COMMIT_FORCE);
|
||||
|
||||
txEnd(tid);
|
||||
|
||||
if (rc)
|
||||
goto error_out;
|
||||
|
||||
/*
|
||||
* map file has been grown now to cover extension to further out;
|
||||
* di_size = new map file size;
|
||||
*
|
||||
* if huge extension, the previous extension based on previous
|
||||
* map file size may not have been sufficient to cover whole extension
|
||||
* (it could have been used up for new map pages),
|
||||
* but the newly grown map file now covers lot bigger new free space
|
||||
* available for further extension of map;
|
||||
*/
|
||||
/* any more blocks to extend ? */
|
||||
if (XSize)
|
||||
goto extendBmap;
|
||||
|
||||
finalizeBmap:
|
||||
/* finalize bmap */
|
||||
dbFinalizeBmap(ipbmap);
|
||||
|
||||
/*
|
||||
* update inode allocation map
|
||||
* ---------------------------
|
||||
*
|
||||
* move iag lists from old to new iag;
|
||||
* agstart field is not updated for logredo() to reconstruct
|
||||
* iag lists if system crash occurs.
|
||||
* (computation of ag number from agstart based on agsize
|
||||
* will correctly identify the new ag);
|
||||
*/
|
||||
/* if new AG size the same as old AG size, done! */
|
||||
if (agsizechanged) {
|
||||
if ((rc = diExtendFS(ipimap, ipbmap)))
|
||||
goto error_out;
|
||||
|
||||
/* finalize imap */
|
||||
if ((rc = diSync(ipimap)))
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* finalize
|
||||
* --------
|
||||
*
|
||||
* extension is committed when on-disk super block is
|
||||
* updated with new descriptors: logredo will recover
|
||||
* crash before it to pre-extension state;
|
||||
*/
|
||||
|
||||
/* sync log to skip log replay of bmap file growth transaction; */
|
||||
/* lmLogSync(log, 1); */
|
||||
|
||||
/*
|
||||
* synchronous write bmap global control page;
|
||||
* for crash before completion of write
|
||||
* logredo() will recover to pre-extendfs state;
|
||||
* for crash after completion of write,
|
||||
* logredo() will recover post-extendfs state;
|
||||
*/
|
||||
if ((rc = dbSync(ipbmap)))
|
||||
goto error_out;
|
||||
|
||||
/*
|
||||
* copy primary bmap inode to secondary bmap inode
|
||||
*/
|
||||
|
||||
ipbmap2 = diReadSpecial(sb, BMAP_I, 1);
|
||||
if (ipbmap2 == NULL) {
|
||||
printk(KERN_ERR "jfs_extendfs: diReadSpecial(bmap) failed\n");
|
||||
goto error_out;
|
||||
}
|
||||
memcpy(&JFS_IP(ipbmap2)->i_xtroot, &JFS_IP(ipbmap)->i_xtroot, 288);
|
||||
ipbmap2->i_size = ipbmap->i_size;
|
||||
ipbmap2->i_blocks = ipbmap->i_blocks;
|
||||
|
||||
diWriteSpecial(ipbmap2, 1);
|
||||
diFreeSpecial(ipbmap2);
|
||||
|
||||
/*
|
||||
* update superblock
|
||||
*/
|
||||
if ((rc = readSuper(sb, &bh)))
|
||||
goto error_out;
|
||||
j_sb = (struct jfs_superblock *)bh->b_data;
|
||||
|
||||
/* mark extendfs() completion */
|
||||
j_sb->s_state &= cpu_to_le32(~FM_EXTENDFS);
|
||||
j_sb->s_size = cpu_to_le64(bmp->db_mapsize <<
|
||||
le16_to_cpu(j_sb->s_l2bfactor));
|
||||
j_sb->s_agsize = cpu_to_le32(bmp->db_agsize);
|
||||
|
||||
/* update inline log space descriptor */
|
||||
if (sbi->mntflag & JFS_INLINELOG) {
|
||||
PXDaddress(&(j_sb->s_logpxd), newLogAddress);
|
||||
PXDlength(&(j_sb->s_logpxd), newLogSize);
|
||||
}
|
||||
|
||||
/* record log's mount serial number */
|
||||
j_sb->s_logserial = cpu_to_le32(log->serial);
|
||||
|
||||
/* update fsck work space descriptor */
|
||||
PXDaddress(&(j_sb->s_fsckpxd), newFSCKAddress);
|
||||
PXDlength(&(j_sb->s_fsckpxd), newFSCKSize);
|
||||
j_sb->s_fscklog = 1;
|
||||
/* sb->s_fsckloglen remains the same */
|
||||
|
||||
/* Update secondary superblock */
|
||||
bh2 = sb_bread(sb, SUPER2_OFF >> sb->s_blocksize_bits);
|
||||
if (bh2) {
|
||||
j_sb2 = (struct jfs_superblock *)bh2->b_data;
|
||||
memcpy(j_sb2, j_sb, sizeof (struct jfs_superblock));
|
||||
|
||||
mark_buffer_dirty(bh);
|
||||
sync_dirty_buffer(bh2);
|
||||
brelse(bh2);
|
||||
}
|
||||
|
||||
/* write primary superblock */
|
||||
mark_buffer_dirty(bh);
|
||||
sync_dirty_buffer(bh);
|
||||
brelse(bh);
|
||||
|
||||
goto resume;
|
||||
|
||||
error_out:
|
||||
jfs_error(sb, "\n");
|
||||
|
||||
resume:
|
||||
/*
|
||||
* resume file system transactions
|
||||
*/
|
||||
txResume(sb);
|
||||
|
||||
out:
|
||||
return rc;
|
||||
}
|
1004
fs/jfs/super.c
Normal file
1004
fs/jfs/super.c
Normal file
File diff suppressed because it is too large
Load diff
52
fs/jfs/symlink.c
Normal file
52
fs/jfs/symlink.c
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (C) Christoph Hellwig, 2001-2002
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/namei.h>
|
||||
#include "jfs_incore.h"
|
||||
#include "jfs_inode.h"
|
||||
#include "jfs_xattr.h"
|
||||
|
||||
static void *jfs_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
char *s = JFS_IP(dentry->d_inode)->i_inline;
|
||||
nd_set_link(nd, s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct inode_operations jfs_fast_symlink_inode_operations = {
|
||||
.readlink = generic_readlink,
|
||||
.follow_link = jfs_follow_link,
|
||||
.setattr = jfs_setattr,
|
||||
.setxattr = jfs_setxattr,
|
||||
.getxattr = jfs_getxattr,
|
||||
.listxattr = jfs_listxattr,
|
||||
.removexattr = jfs_removexattr,
|
||||
};
|
||||
|
||||
const struct inode_operations jfs_symlink_inode_operations = {
|
||||
.readlink = generic_readlink,
|
||||
.follow_link = page_follow_link_light,
|
||||
.put_link = page_put_link,
|
||||
.setattr = jfs_setattr,
|
||||
.setxattr = jfs_setxattr,
|
||||
.getxattr = jfs_getxattr,
|
||||
.listxattr = jfs_listxattr,
|
||||
.removexattr = jfs_removexattr,
|
||||
};
|
||||
|
1106
fs/jfs/xattr.c
Normal file
1106
fs/jfs/xattr.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue