#include <linux/writeback.h>
#include <linux/mpage.h>
#include <linux/uio.h>
+#include <linux/vserver/xid.h>
#include "xattr.h"
#include "acl.h"
{
int err;
+ might_sleep();
+
BUFFER_TRACE(bh, "enter");
jbd_debug(4, "forgetting bh %p: is_metadata = %d, mode %o, "
return ext3_journal_restart(handle, blocks_for_truncate(inode));
}
+static void ext3_truncate_nocheck (struct inode *inode);
+
+/*
+ * Called at each iput()
+ *
+ * The inode may be "bad" if ext3_read_inode() saw an error from
+ * ext3_get_inode(), so we need to check that to avoid freeing random disk
+ * blocks.
+ */
+void ext3_put_inode(struct inode *inode)
+{
+ if (!is_bad_inode(inode))
+ ext3_discard_prealloc(inode);
+}
+
/*
* Called at the last iput() if i_nlink is zero.
*/
handle->h_sync = 1;
inode->i_size = 0;
if (inode->i_blocks)
- ext3_truncate(inode);
+ ext3_truncate_nocheck(inode);
/*
* Kill off the orphan record which ext3_truncate created.
* AKPM: I think this can be inside the above `if'.
clear_inode(inode); /* We must guarantee clearing of inode... */
}
+void ext3_discard_prealloc (struct inode * inode)
+{
+#ifdef EXT3_PREALLOCATE
+ struct ext3_inode_info *ei = EXT3_I(inode);
+ /* Writer: ->i_prealloc* */
+ if (ei->i_prealloc_count) {
+ unsigned short total = ei->i_prealloc_count;
+ unsigned long block = ei->i_prealloc_block;
+ ei->i_prealloc_count = 0;
+ ei->i_prealloc_block = 0;
+ /* Writer: end */
+ ext3_free_blocks (inode, block, total);
+ }
+#endif
+}
+
static int ext3_alloc_block (handle_t *handle,
struct inode * inode, unsigned long goal, int *err)
{
unsigned long result;
- result = ext3_new_block (handle, inode, goal, err);
+#ifdef EXT3_PREALLOCATE
+#ifdef EXT3FS_DEBUG
+ static unsigned long alloc_hits, alloc_attempts;
+#endif
+ struct ext3_inode_info *ei = EXT3_I(inode);
+ /* Writer: ->i_prealloc* */
+ if (ei->i_prealloc_count &&
+ (goal == ei->i_prealloc_block ||
+ goal + 1 == ei->i_prealloc_block))
+ {
+ result = ei->i_prealloc_block++;
+ ei->i_prealloc_count--;
+ /* Writer: end */
+ ext3_debug ("preallocation hit (%lu/%lu).\n",
+ ++alloc_hits, ++alloc_attempts);
+ } else {
+ ext3_discard_prealloc (inode);
+ ext3_debug ("preallocation miss (%lu/%lu).\n",
+ alloc_hits, ++alloc_attempts);
+ if (S_ISREG(inode->i_mode))
+ result = ext3_new_block (inode, goal,
+ &ei->i_prealloc_count,
+ &ei->i_prealloc_block, err);
+ else
+ result = ext3_new_block(inode, goal, NULL, NULL, err);
+ /*
+ * AKPM: this is somewhat sticky. I'm not surprised it was
+ * disabled in 2.2's ext3. Need to integrate b_committed_data
+ * guarding with preallocation, if indeed preallocation is
+ * effective.
+ */
+ }
+#else
+ result = ext3_new_block(handle, inode, goal, NULL, NULL, err);
+#endif
return result;
}
static int ext3_get_block(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create)
{
- handle_t *handle = 0;
+ handle_t *handle = NULL;
int ret;
if (create) {
handle_t *handle = journal_current_handle();
int ret = 0;
- if (handle && handle->h_buffer_credits <= EXT3_RESERVE_TRANS_BLOCKS) {
+ if (!handle)
+ goto get_block; /* A read */
+
+ if (handle->h_transaction->t_state == T_LOCKED) {
+ /*
+ * Huge direct-io writes can hold off commits for long
+ * periods of time. Let this commit run.
+ */
+ ext3_journal_stop(handle);
+ handle = ext3_journal_start(inode, DIO_CREDITS);
+ if (IS_ERR(handle))
+ ret = PTR_ERR(handle);
+ goto get_block;
+ }
+
+ if (handle->h_buffer_credits <= EXT3_RESERVE_TRANS_BLOCKS) {
/*
* Getting low on buffer credits...
*/
- if (!ext3_journal_extend(handle, DIO_CREDITS)) {
+ ret = ext3_journal_extend(handle, DIO_CREDITS);
+ if (ret > 0) {
/*
- * Couldn't extend the transaction. Start a new one
+ * Couldn't extend the transaction. Start a new one.
*/
ret = ext3_journal_restart(handle, DIO_CREDITS);
}
}
+
+get_block:
if (ret == 0)
ret = ext3_get_block_handle(handle, inode, iblock,
bh_result, create, 0);
- if (ret == 0)
- bh_result->b_size = (1 << inode->i_blkbits);
+ bh_result->b_size = (1 << inode->i_blkbits);
return ret;
}
-
/*
* `handle' can be NULL if create is zero
*/
bh = ext3_getblk (handle, inode, block, create, err);
if (!bh)
return bh;
+#ifdef EXT3_PREALLOCATE
+ /*
+ * If the inode has grown, and this is a directory, then use a few
+ * more of the preallocated blocks to keep directory fragmentation
+ * down. The preallocated blocks are guaranteed to be contiguous.
+ */
+ if (create &&
+ S_ISDIR(inode->i_mode) &&
+ inode->i_blocks > prev_blocks &&
+ EXT3_HAS_COMPAT_FEATURE(inode->i_sb,
+ EXT3_FEATURE_COMPAT_DIR_PREALLOC)) {
+ int i;
+ struct buffer_head *tmp_bh;
+
+ for (i = 1;
+ EXT3_I(inode)->i_prealloc_count &&
+ i < EXT3_SB(inode->i_sb)->s_es->s_prealloc_dir_blocks;
+ i++) {
+ /*
+ * ext3_getblk will zero out the contents of the
+ * directory for us
+ */
+ tmp_bh = ext3_getblk(handle, inode,
+ block+i, create, err);
+ if (!tmp_bh) {
+ brelse (bh);
+ return 0;
+ }
+ brelse (tmp_bh);
+ }
+ }
+#endif
if (buffer_uptodate(bh))
return bh;
ll_rw_block (READ, 1, &bh);
* ext3_truncate() run will find them and release them.
*/
-void ext3_truncate(struct inode * inode)
+void ext3_truncate_nocheck(struct inode * inode)
{
handle_t *handle;
struct ext3_inode_info *ei = EXT3_I(inode);
return;
if (ext3_inode_is_fast_symlink(inode))
return;
- if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
- return;
- ext3_discard_reservation(inode);
+ ext3_discard_prealloc(inode);
/*
* We have to lock the EOF page here, because lock_page() nests
unsigned long offset, block;
struct buffer_head *bh;
struct ext3_group_desc * gdp;
-
if ((ino != EXT3_ROOT_INO &&
ino != EXT3_JOURNAL_INO &&
- ino != EXT3_RESIZE_INO &&
ino < EXT3_FIRST_INO(sb)) ||
ino > le32_to_cpu(
EXT3_SB(sb)->s_es->s_inodes_count)) {
return 0;
}
+void ext3_truncate(struct inode * inode)
+{
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+ return;
+ ext3_truncate_nocheck(inode);
+}
+
void ext3_set_inode_flags(struct inode *inode)
{
unsigned int flags = EXT3_I(inode)->i_flags;
- inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);
+ inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_IUNLINK|S_BARRIER|S_NOATIME|S_DIRSYNC);
if (flags & EXT3_SYNC_FL)
inode->i_flags |= S_SYNC;
if (flags & EXT3_APPEND_FL)
inode->i_flags |= S_APPEND;
if (flags & EXT3_IMMUTABLE_FL)
inode->i_flags |= S_IMMUTABLE;
+ if (flags & EXT3_IUNLINK_FL)
+ inode->i_flags |= S_IUNLINK;
+ if (flags & EXT3_BARRIER_FL)
+ inode->i_flags |= S_BARRIER;
if (flags & EXT3_NOATIME_FL)
inode->i_flags |= S_NOATIME;
if (flags & EXT3_DIRSYNC_FL)
struct ext3_inode_info *ei = EXT3_I(inode);
struct buffer_head *bh;
int block;
+ uid_t uid;
+ gid_t gid;
#ifdef CONFIG_EXT3_FS_POSIX_ACL
ei->i_acl = EXT3_ACL_NOT_CACHED;
bh = iloc.bh;
raw_inode = ext3_raw_inode(&iloc);
inode->i_mode = le16_to_cpu(raw_inode->i_mode);
- inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
- inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
+ uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
+ gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
if(!(test_opt (inode->i_sb, NO_UID32))) {
- inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
- inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
+ uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
+ gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
}
+ inode->i_uid = INOXID_UID(XID_TAG(inode), uid, gid);
+ inode->i_gid = INOXID_GID(XID_TAG(inode), uid, gid);
+ inode->i_xid = INOXID_XID(XID_TAG(inode), uid, gid,
+ le16_to_cpu(raw_inode->i_raw_xid));
+
inode->i_nlink = le16_to_cpu(raw_inode->i_links_count);
inode->i_size = le32_to_cpu(raw_inode->i_size);
inode->i_atime.tv_sec = le32_to_cpu(raw_inode->i_atime);
}
ei->i_disksize = inode->i_size;
inode->i_generation = le32_to_cpu(raw_inode->i_generation);
+#ifdef EXT3_PREALLOCATE
+ ei->i_prealloc_count = 0;
+#endif
ei->i_block_group = iloc.block_group;
- ei->i_rsv_window.rsv_start = 0;
- ei->i_rsv_window.rsv_end= 0;
- atomic_set(&ei->i_rsv_window.rsv_goal_size, EXT3_DEFAULT_RESERVE_BLOCKS);
- INIT_LIST_HEAD(&ei->i_rsv_window.rsv_list);
+
/*
* NOTE! The in-memory inode i_data array is in little-endian order
* even on big-endian machines: we do NOT byteswap the block numbers!
struct ext3_inode *raw_inode = ext3_raw_inode(iloc);
struct ext3_inode_info *ei = EXT3_I(inode);
struct buffer_head *bh = iloc->bh;
+ uid_t uid = XIDINO_UID(XID_TAG(inode), inode->i_uid, inode->i_xid);
+ gid_t gid = XIDINO_GID(XID_TAG(inode), inode->i_gid, inode->i_xid);
int err = 0, rc, block;
/* For fields not not tracking in the in-memory inode,
raw_inode->i_mode = cpu_to_le16(inode->i_mode);
if(!(test_opt(inode->i_sb, NO_UID32))) {
- raw_inode->i_uid_low = cpu_to_le16(low_16_bits(inode->i_uid));
- raw_inode->i_gid_low = cpu_to_le16(low_16_bits(inode->i_gid));
+ raw_inode->i_uid_low = cpu_to_le16(low_16_bits(uid));
+ raw_inode->i_gid_low = cpu_to_le16(low_16_bits(gid));
/*
* Fix up interoperability with old kernels. Otherwise, old inodes get
* re-used with the upper 16 bits of the uid/gid intact
*/
if(!ei->i_dtime) {
raw_inode->i_uid_high =
- cpu_to_le16(high_16_bits(inode->i_uid));
+ cpu_to_le16(high_16_bits(uid));
raw_inode->i_gid_high =
- cpu_to_le16(high_16_bits(inode->i_gid));
+ cpu_to_le16(high_16_bits(gid));
} else {
raw_inode->i_uid_high = 0;
raw_inode->i_gid_high = 0;
}
} else {
raw_inode->i_uid_low =
- cpu_to_le16(fs_high2lowuid(inode->i_uid));
+ cpu_to_le16(fs_high2lowuid(uid));
raw_inode->i_gid_low =
- cpu_to_le16(fs_high2lowgid(inode->i_gid));
+ cpu_to_le16(fs_high2lowgid(gid));
raw_inode->i_uid_high = 0;
raw_inode->i_gid_high = 0;
}
+#ifdef CONFIG_INOXID_GID32
+ raw_inode->i_raw_xid = cpu_to_le16(inode->i_xid);
+#endif
raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
raw_inode->i_size = cpu_to_le32(ei->i_disksize);
raw_inode->i_atime = cpu_to_le32(inode->i_atime.tv_sec);
ext3_force_commit(inode->i_sb);
}
+int ext3_setattr_flags(struct inode *inode, unsigned int flags)
+{
+ unsigned int oldflags, newflags;
+ int err = 0;
+
+ oldflags = EXT3_I(inode)->i_flags;
+ newflags = oldflags &
+ ~(EXT3_IMMUTABLE_FL | EXT3_IUNLINK_FL | EXT3_BARRIER_FL);
+ if (flags & ATTR_FLAG_IMMUTABLE)
+ newflags |= EXT3_IMMUTABLE_FL;
+ if (flags & ATTR_FLAG_IUNLINK)
+ newflags |= EXT3_IUNLINK_FL;
+ if (flags & ATTR_FLAG_BARRIER)
+ newflags |= EXT3_BARRIER_FL;
+
+ if (oldflags ^ newflags) {
+ handle_t *handle;
+ struct ext3_iloc iloc;
+
+ handle = ext3_journal_start(inode, 1);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (IS_SYNC(inode))
+ handle->h_sync = 1;
+ err = ext3_reserve_inode_write(handle, inode, &iloc);
+ if (err)
+ goto flags_err;
+
+ EXT3_I(inode)->i_flags = newflags;
+ inode->i_ctime = CURRENT_TIME;
+
+ err = ext3_mark_iloc_dirty(handle, inode, &iloc);
+ flags_err:
+ ext3_journal_stop(handle);
+ }
+ return err;
+}
+
/*
* ext3_setattr()
*
return error;
if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
- (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) {
+ (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid) ||
+ (ia_valid & ATTR_XID && attr->ia_xid != inode->i_xid)) {
handle_t *handle;
/* (user+group)*(old+new) structure, inode write (sb,
inode->i_uid = attr->ia_uid;
if (attr->ia_valid & ATTR_GID)
inode->i_gid = attr->ia_gid;
+ if ((attr->ia_valid & ATTR_XID)
+ && inode->i_sb
+ && (inode->i_sb->s_flags & MS_TAGXID))
+ inode->i_xid = attr->ia_xid;
error = ext3_mark_inode_dirty(handle, inode);
ext3_journal_stop(handle);
}
ext3_journal_stop(handle);
}
+ if (ia_valid & ATTR_ATTR_FLAG) {
+ rc = ext3_setattr_flags(inode, attr->ia_attr_flags);
+ if (!error)
+ error = rc;
+ }
+
rc = inode_setattr(inode, attr);
/* If inode_setattr's call to ext3_truncate failed to get a
struct ext3_iloc iloc;
int err;
+ might_sleep();
err = ext3_reserve_inode_write(handle, inode, &iloc);
if (!err)
err = ext3_mark_iloc_dirty(handle, inode, &iloc);