Commit 412dd3a6 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'xfs-for-linus-3.16-rc1' of git://oss.sgi.com/xfs/xfs

Pull xfs updates from Dave Chinner:
 "This update contains:
   - cleanup removing unused function args
   - rework of the filestreams allocator to use dentry cache parent
     lookups
   - new on-disk free inode btree and optimised inode allocator
   - various bug fixes
   - rework of internal attribute API
   - cleanup of superblock feature bit support to remove historic cruft
   - more fixes and minor cleanups
   - added a new directory/attribute geometry abstraction
   - yet more fixes and minor cleanups"

* tag 'xfs-for-linus-3.16-rc1' of git://oss.sgi.com/xfs/xfs: (86 commits)
  xfs: fix xfs_da_args sparse warning in xfs_readdir
  xfs: Fix rounding in xfs_alloc_fix_len()
  xfs: tone down writepage/releasepage WARN_ONs
  xfs: small cleanup in xfs_lowbit64()
  xfs: kill xfs_buf_geterror()
  xfs: xfs_readsb needs to check for magic numbers
  xfs: block allocation work needs to be kswapd aware
  xfs: remove redundant geometry information from xfs_da_state
  xfs: replace attr LBSIZE with xfs_da_geometry
  xfs: pass xfs_da_args to xfs_attr_leaf_newentsize
  xfs: use xfs_da_geometry for block size in attr code
  xfs: remove mp->m_dir_geo from directory logging
  xfs: reduce direct usage of mp->m_dir_geo
  xfs: move node entry counts to xfs_da_geometry
  xfs: convert dir/attr btree threshold to xfs_da_geometry
  xfs: convert m_dirblksize to xfs_da_geometry
  xfs: convert m_dirblkfsbs to xfs_da_geometry
  xfs: convert directory segment limits to xfs_da_geometry
  xfs: convert directory db conversion to xfs_da_geometry
  xfs: convert directory dablk conversion to xfs_da_geometry
  ...
parents 23d4ed53 7691283d
......@@ -278,6 +278,17 @@ static int quota_getxquota(struct super_block *sb, int type, qid_t id,
return ret;
}
static int quota_rmxquota(struct super_block *sb, void __user *addr)
{
__u32 flags;
if (copy_from_user(&flags, addr, sizeof(flags)))
return -EFAULT;
if (!sb->s_qcop->rm_xquota)
return -ENOSYS;
return sb->s_qcop->rm_xquota(sb, flags);
}
/* Copy parameters and call proper function */
static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,
void __user *addr, struct path *path)
......@@ -316,8 +327,9 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,
return sb->s_qcop->quota_sync(sb, type);
case Q_XQUOTAON:
case Q_XQUOTAOFF:
case Q_XQUOTARM:
return quota_setxstate(sb, cmd, addr);
case Q_XQUOTARM:
return quota_rmxquota(sb, addr);
case Q_XGETQSTAT:
return quota_getxstate(sb, addr);
case Q_XGETQSTATV:
......
......@@ -160,30 +160,38 @@ typedef struct xfs_agi {
* still being referenced.
*/
__be32 agi_unlinked[XFS_AGI_UNLINKED_BUCKETS];
/*
* This marks the end of logging region 1 and start of logging region 2.
*/
uuid_t agi_uuid; /* uuid of filesystem */
__be32 agi_crc; /* crc of agi sector */
__be32 agi_pad32;
__be64 agi_lsn; /* last write sequence */
__be32 agi_free_root; /* root of the free inode btree */
__be32 agi_free_level;/* levels in free inode btree */
/* structure must be padded to 64 bit alignment */
} xfs_agi_t;
#define XFS_AGI_CRC_OFF offsetof(struct xfs_agi, agi_crc)
#define XFS_AGI_MAGICNUM 0x00000001
#define XFS_AGI_VERSIONNUM 0x00000002
#define XFS_AGI_SEQNO 0x00000004
#define XFS_AGI_LENGTH 0x00000008
#define XFS_AGI_COUNT 0x00000010
#define XFS_AGI_ROOT 0x00000020
#define XFS_AGI_LEVEL 0x00000040
#define XFS_AGI_FREECOUNT 0x00000080
#define XFS_AGI_NEWINO 0x00000100
#define XFS_AGI_DIRINO 0x00000200
#define XFS_AGI_UNLINKED 0x00000400
#define XFS_AGI_NUM_BITS 11
#define XFS_AGI_ALL_BITS ((1 << XFS_AGI_NUM_BITS) - 1)
#define XFS_AGI_MAGICNUM (1 << 0)
#define XFS_AGI_VERSIONNUM (1 << 1)
#define XFS_AGI_SEQNO (1 << 2)
#define XFS_AGI_LENGTH (1 << 3)
#define XFS_AGI_COUNT (1 << 4)
#define XFS_AGI_ROOT (1 << 5)
#define XFS_AGI_LEVEL (1 << 6)
#define XFS_AGI_FREECOUNT (1 << 7)
#define XFS_AGI_NEWINO (1 << 8)
#define XFS_AGI_DIRINO (1 << 9)
#define XFS_AGI_UNLINKED (1 << 10)
#define XFS_AGI_NUM_BITS_R1 11 /* end of the 1st agi logging region */
#define XFS_AGI_ALL_BITS_R1 ((1 << XFS_AGI_NUM_BITS_R1) - 1)
#define XFS_AGI_FREE_ROOT (1 << 11)
#define XFS_AGI_FREE_LEVEL (1 << 12)
#define XFS_AGI_NUM_BITS_R2 13
/* disk block (xfs_daddr_t) in the AG */
#define XFS_AGI_DADDR(mp) ((xfs_daddr_t)(2 << (mp)->m_sectbb_log))
......
......@@ -257,16 +257,14 @@ xfs_alloc_fix_len(
k = rlen % args->prod;
if (k == args->mod)
return;
if (k > args->mod) {
if ((int)(rlen = rlen - k - args->mod) < (int)args->minlen)
return;
} else {
if ((int)(rlen = rlen - args->prod - (args->mod - k)) <
(int)args->minlen)
return;
}
ASSERT(rlen >= args->minlen);
ASSERT(rlen <= args->maxlen);
if (k > args->mod)
rlen = rlen - (k - args->mod);
else
rlen = rlen - args->prod + (args->mod - k);
if ((int)rlen < (int)args->minlen)
return;
ASSERT(rlen >= args->minlen && rlen <= args->maxlen);
ASSERT(rlen % args->prod == args->mod);
args->len = rlen;
}
......@@ -541,7 +539,6 @@ xfs_alloc_read_agfl(
XFS_FSS_TO_BB(mp, 1), 0, &bp, &xfs_agfl_buf_ops);
if (error)
return error;
ASSERT(!xfs_buf_geterror(bp));
xfs_buf_set_ref(bp, XFS_AGFL_REF);
*bpp = bp;
return 0;
......
......@@ -70,7 +70,6 @@ xfs_allocbt_alloc_block(
struct xfs_btree_cur *cur,
union xfs_btree_ptr *start,
union xfs_btree_ptr *new,
int length,
int *stat)
{
int error;
......
......@@ -975,14 +975,39 @@ xfs_vm_writepage(
* Given that we do not allow direct reclaim to call us, we should
* never be called while in a filesystem transaction.
*/
if (WARN_ON(current->flags & PF_FSTRANS))
if (WARN_ON_ONCE(current->flags & PF_FSTRANS))
goto redirty;
/* Is this page beyond the end of the file? */
offset = i_size_read(inode);
end_index = offset >> PAGE_CACHE_SHIFT;
last_index = (offset - 1) >> PAGE_CACHE_SHIFT;
if (page->index >= end_index) {
/*
* The page index is less than the end_index, adjust the end_offset
* to the highest offset that this page should represent.
* -----------------------------------------------------
* | file mapping | <EOF> |
* -----------------------------------------------------
* | Page ... | Page N-2 | Page N-1 | Page N | |
* ^--------------------------------^----------|--------
* | desired writeback range | see else |
* ---------------------------------^------------------|
*/
if (page->index < end_index)
end_offset = (xfs_off_t)(page->index + 1) << PAGE_CACHE_SHIFT;
else {
/*
* Check whether the page to write out is beyond or straddles
* i_size or not.
* -------------------------------------------------------
* | file mapping | <EOF> |
* -------------------------------------------------------
* | Page ... | Page N-2 | Page N-1 | Page N | Beyond |
* ^--------------------------------^-----------|---------
* | | Straddles |
* ---------------------------------^-----------|--------|
*/
unsigned offset_into_page = offset & (PAGE_CACHE_SIZE - 1);
/*
......@@ -990,24 +1015,36 @@ xfs_vm_writepage(
* truncate operation that is in progress. We must redirty the
* page so that reclaim stops reclaiming it. Otherwise
* xfs_vm_releasepage() is called on it and gets confused.
*
* Note that the end_index is unsigned long, it would overflow
* if the given offset is greater than 16TB on 32-bit system
* and if we do check the page is fully outside i_size or not
* via "if (page->index >= end_index + 1)" as "end_index + 1"
* will be evaluated to 0. Hence this page will be redirtied
* and be written out repeatedly which would result in an
* infinite loop, the user program that perform this operation
* will hang. Instead, we can verify this situation by checking
* if the page to write is totally beyond the i_size or if it's
* offset is just equal to the EOF.
*/
if (page->index >= end_index + 1 || offset_into_page == 0)
if (page->index > end_index ||
(page->index == end_index && offset_into_page == 0))
goto redirty;
/*
* The page straddles i_size. It must be zeroed out on each
* and every writepage invocation because it may be mmapped.
* "A file is mapped in multiples of the page size. For a file
* that is not a multiple of the page size, the remaining
* that is not a multiple of the page size, the remaining
* memory is zeroed when mapped, and writes to that region are
* not written out to the file."
*/
zero_user_segment(page, offset_into_page, PAGE_CACHE_SIZE);
/* Adjust the end_offset to the end of file */
end_offset = offset;
}
end_offset = min_t(unsigned long long,
(xfs_off_t)(page->index + 1) << PAGE_CACHE_SHIFT,
offset);
len = 1 << inode->i_blkbits;
bh = head = page_buffers(page);
......@@ -1188,9 +1225,9 @@ xfs_vm_releasepage(
xfs_count_page_state(page, &delalloc, &unwritten);
if (WARN_ON(delalloc))
if (WARN_ON_ONCE(delalloc))
return 0;
if (WARN_ON(unwritten))
if (WARN_ON_ONCE(unwritten))
return 0;
return try_to_free_buffers(page);
......
......@@ -77,17 +77,27 @@ STATIC int xfs_attr_refillstate(xfs_da_state_t *state);
STATIC int
xfs_attr_name_to_xname(
struct xfs_name *xname,
const unsigned char *aname)
xfs_attr_args_init(
struct xfs_da_args *args,
struct xfs_inode *dp,
const unsigned char *name,
int flags)
{
if (!aname)
if (!name)
return EINVAL;
xname->name = aname;
xname->len = strlen((char *)aname);
if (xname->len >= MAXNAMELEN)
memset(args, 0, sizeof(*args));
args->geo = dp->i_mount->m_attr_geo;
args->whichfork = XFS_ATTR_FORK;
args->dp = dp;
args->flags = flags;
args->name = name;
args->namelen = strlen((const char *)name);
if (args->namelen >= MAXNAMELEN)
return EFAULT; /* match IRIX behaviour */
args->hashval = xfs_da_hashname(args->name, args->namelen);
return 0;
}
......@@ -106,79 +116,46 @@ xfs_inode_hasattr(
* Overall external interface routines.
*========================================================================*/
STATIC int
xfs_attr_get_int(
int
xfs_attr_get(
struct xfs_inode *ip,
struct xfs_name *name,
const unsigned char *name,
unsigned char *value,
int *valuelenp,
int flags)
{
xfs_da_args_t args;
int error;
struct xfs_da_args args;
uint lock_mode;
int error;
XFS_STATS_INC(xs_attr_get);
if (XFS_FORCED_SHUTDOWN(ip->i_mount))
return EIO;
if (!xfs_inode_hasattr(ip))
return ENOATTR;
/*
* Fill in the arg structure for this request.
*/
memset((char *)&args, 0, sizeof(args));
args.name = name->name;
args.namelen = name->len;
error = xfs_attr_args_init(&args, ip, name, flags);
if (error)
return error;
args.value = value;
args.valuelen = *valuelenp;
args.flags = flags;
args.hashval = xfs_da_hashname(args.name, args.namelen);
args.dp = ip;
args.whichfork = XFS_ATTR_FORK;
/*
* Decide on what work routines to call based on the inode size.
*/
if (ip->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
lock_mode = xfs_ilock_attr_map_shared(ip);
if (!xfs_inode_hasattr(ip))
error = ENOATTR;
else if (ip->i_d.di_aformat == XFS_DINODE_FMT_LOCAL)
error = xfs_attr_shortform_getvalue(&args);
} else if (xfs_bmap_one_block(ip, XFS_ATTR_FORK)) {
else if (xfs_bmap_one_block(ip, XFS_ATTR_FORK))
error = xfs_attr_leaf_get(&args);
} else {
else
error = xfs_attr_node_get(&args);
}
xfs_iunlock(ip, lock_mode);
/*
* Return the number of bytes in the value to the caller.
*/
*valuelenp = args.valuelen;
if (error == EEXIST)
error = 0;
return(error);
}
int
xfs_attr_get(
xfs_inode_t *ip,
const unsigned char *name,
unsigned char *value,
int *valuelenp,
int flags)
{
int error;
struct xfs_name xname;
uint lock_mode;
XFS_STATS_INC(xs_attr_get);
if (XFS_FORCED_SHUTDOWN(ip->i_mount))
return(EIO);
error = xfs_attr_name_to_xname(&xname, name);
if (error)
return error;
lock_mode = xfs_ilock_attr_map_shared(ip);
error = xfs_attr_get_int(ip, &xname, value, valuelenp, flags);
xfs_iunlock(ip, lock_mode);
return(error);
return error == EEXIST ? 0 : error;
}
/*
......@@ -186,12 +163,10 @@ xfs_attr_get(
*/
STATIC int
xfs_attr_calc_size(
struct xfs_inode *ip,
int namelen,
int valuelen,
struct xfs_da_args *args,
int *local)
{
struct xfs_mount *mp = ip->i_mount;
struct xfs_mount *mp = args->dp->i_mount;
int size;
int nblks;
......@@ -199,12 +174,10 @@ xfs_attr_calc_size(
* Determine space new attribute will use, and if it would be
* "local" or "remote" (note: local != inline).
*/
size = xfs_attr_leaf_newentsize(namelen, valuelen,
mp->m_sb.sb_blocksize, local);
size = xfs_attr_leaf_newentsize(args, local);
nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK);
if (*local) {
if (size > (mp->m_sb.sb_blocksize >> 1)) {
if (size > (args->geo->blksize / 2)) {
/* Double split possible */
nblks *= 2;
}
......@@ -213,7 +186,7 @@ xfs_attr_calc_size(
* Out of line attribute, cannot double split, but
* make room for the attribute value itself.
*/
uint dblocks = xfs_attr3_rmt_blocks(mp, valuelen);
uint dblocks = xfs_attr3_rmt_blocks(mp, args->valuelen);
nblks += dblocks;
nblks += XFS_NEXTENTADD_SPACE_RES(mp, dblocks, XFS_ATTR_FORK);
}
......@@ -221,26 +194,38 @@ xfs_attr_calc_size(
return nblks;
}
STATIC int
xfs_attr_set_int(
struct xfs_inode *dp,
struct xfs_name *name,
unsigned char *value,
int valuelen,
int flags)
int
xfs_attr_set(
struct xfs_inode *dp,
const unsigned char *name,
unsigned char *value,
int valuelen,
int flags)
{
xfs_da_args_t args;
xfs_fsblock_t firstblock;
xfs_bmap_free_t flist;
int error, err2, committed;
struct xfs_mount *mp = dp->i_mount;
struct xfs_da_args args;
struct xfs_bmap_free flist;
struct xfs_trans_res tres;
xfs_fsblock_t firstblock;
int rsvd = (flags & ATTR_ROOT) != 0;
int local;
int error, err2, committed, local;
XFS_STATS_INC(xs_attr_set);
if (XFS_FORCED_SHUTDOWN(dp->i_mount))
return EIO;
error = xfs_attr_args_init(&args, dp, name, flags);
if (error)
return error;
args.value = value;
args.valuelen = valuelen;
args.firstblock = &firstblock;
args.flist = &flist;
args.op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT;
args.total = xfs_attr_calc_size(&args, &local);
/*
* Attach the dquots to the inode.
*/
error = xfs_qm_dqattach(dp, 0);
if (error)
return error;
......@@ -251,31 +236,13 @@ xfs_attr_set_int(
*/
if (XFS_IFORK_Q(dp) == 0) {
int sf_size = sizeof(xfs_attr_sf_hdr_t) +
XFS_ATTR_SF_ENTSIZE_BYNAME(name->len, valuelen);
XFS_ATTR_SF_ENTSIZE_BYNAME(args.namelen, valuelen);
if ((error = xfs_bmap_add_attrfork(dp, sf_size, rsvd)))
return(error);
error = xfs_bmap_add_attrfork(dp, sf_size, rsvd);
if (error)
return error;
}
/*
* Fill in the arg structure for this request.
*/
memset((char *)&args, 0, sizeof(args));
args.name = name->name;
args.namelen = name->len;
args.value = value;
args.valuelen = valuelen;
args.flags = flags;
args.hashval = xfs_da_hashname(args.name, args.namelen);
args.dp = dp;
args.firstblock = &firstblock;
args.flist = &flist;
args.whichfork = XFS_ATTR_FORK;
args.op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT;
/* Size is now blocks for attribute data */
args.total = xfs_attr_calc_size(dp, name->len, valuelen, &local);
/*
* Start our first transaction of the day.
*
......@@ -303,7 +270,7 @@ xfs_attr_set_int(
error = xfs_trans_reserve(args.trans, &tres, args.total, 0);
if (error) {
xfs_trans_cancel(args.trans, 0);
return(error);
return error;
}
xfs_ilock(dp, XFS_ILOCK_EXCL);
......@@ -313,7 +280,7 @@ xfs_attr_set_int(
if (error) {
xfs_iunlock(dp, XFS_ILOCK_EXCL);
xfs_trans_cancel(args.trans, XFS_TRANS_RELEASE_LOG_RES);
return (error);
return error;
}
xfs_trans_ijoin(args.trans, dp, 0);
......@@ -322,9 +289,9 @@ xfs_attr_set_int(
* If the attribute list is non-existent or a shortform list,
* upgrade it to a single-leaf-block attribute list.
*/
if ((dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) ||
((dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS) &&
(dp->i_d.di_anextents == 0))) {
if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
(dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
dp->i_d.di_anextents == 0)) {
/*
* Build initial attribute list (if required).
......@@ -349,9 +316,8 @@ xfs_attr_set_int(
* the transaction goes to disk before returning
* to the user.
*/
if (mp->m_flags & XFS_MOUNT_WSYNC) {
if (mp->m_flags & XFS_MOUNT_WSYNC)
xfs_trans_set_sync(args.trans);
}
if (!error && (flags & ATTR_KERNOTIME) == 0) {
xfs_trans_ichgtime(args.trans, dp,
......@@ -361,7 +327,7 @@ xfs_attr_set_int(
XFS_TRANS_RELEASE_LOG_RES);
xfs_iunlock(dp, XFS_ILOCK_EXCL);
return(error == 0 ? err2 : error);
return error ? error : err2;
}
/*
......@@ -399,22 +365,19 @@ xfs_attr_set_int(
}
if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
error = xfs_attr_leaf_addname(&args);
} else {
else
error = xfs_attr_node_addname(&args);
}
if (error) {
if (error)
goto out;
}
/*
* If this is a synchronous mount, make sure that the
* transaction goes to disk before returning to the user.
*/
if (mp->m_flags & XFS_MOUNT_WSYNC) {
if (mp->m_flags & XFS_MOUNT_WSYNC)
xfs_trans_set_sync(args.trans);
}
if ((flags & ATTR_KERNOTIME) == 0)
xfs_trans_ichgtime(args.trans, dp, XFS_ICHGTIME_CHG);
......@@ -426,65 +389,47 @@ xfs_attr_set_int(
error = xfs_trans_commit(args.trans, XFS_TRANS_RELEASE_LOG_RES);
xfs_iunlock(dp, XFS_ILOCK_EXCL);
return(error);
return error;
out:
if (args.trans)
if (args.trans) {
xfs_trans_cancel(args.trans,
XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
}
xfs_iunlock(dp, XFS_ILOCK_EXCL);
return(error);
return error;
}
/*
* Generic handler routine to remove a name from an attribute list.
* Transitions attribute list from Btree to shortform as necessary.
*/
int
xfs_attr_set(
xfs_inode_t *dp,
const unsigned char *name,
unsigned char *value,
int valuelen,
int flags)
xfs_attr_remove(
struct xfs_inode *dp,
const unsigned char *name,
int flags)
{
int error;
struct xfs_name xname;
struct xfs_mount *mp = dp->i_mount;
struct xfs_da_args args;
struct xfs_bmap_free flist;
xfs_fsblock_t firstblock;
int error;
XFS_STATS_INC(xs_attr_set);
XFS_STATS_INC(xs_attr_remove);
if (XFS_FORCED_SHUTDOWN(dp->i_mount))
return (EIO);
return EIO;
if (!xfs_inode_hasattr(dp))
return ENOATTR;
error = xfs_attr_name_to_xname(&xname, name);
error = xfs_attr_args_init(&args, dp, name, flags);
if (error)
return error;
return xfs_attr_set_int(dp, &xname, value, valuelen, flags);
}
/*
* Generic handler routine to remove a name from an attribute list.
* Transitions attribute list from Btree to shortform as necessary.
*/
STATIC int
xfs_attr_remove_int(xfs_inode_t *dp, struct xfs_name *name, int flags)
{
xfs_da_args_t args;
xfs_fsblock_t firstblock;
xfs_bmap_free_t flist;
int error;