Commit ae90fb14 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'xfs-for-linus-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dgc/linux-xfs

Pull xfs update from Dave Chinner:
 "This update contains:

   - RENAME_EXCHANGE support

   - Rework of the superblock logging infrastructure

   - Rework of the XFS_IOCTL_SETXATTR implementation
       * enables use inside user namespaces
       * fixes inconsistencies setting extent size hints

   - fixes for missing buffer type annotations used in log recovery

   - more consolidation of libxfs headers

   - preparation patches for block based PNFS support

   - miscellaneous bug fixes and cleanups"

* tag 'xfs-for-linus-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dgc/linux-xfs: (37 commits)
  xfs: only trace buffer items if they exist
  xfs: report proper f_files in statfs if we overshoot imaxpct
  xfs: fix panic_mask documentation
  xfs: xfs_ioctl_setattr_check_projid can be static
  xfs: growfs should use synchronous transactions
  xfs: fix behaviour of XFS_IOC_FSSETXATTR on directories
  xfs: factor projid hint checking out of xfs_ioctl_setattr
  xfs: factor extsize hint checking out of xfs_ioctl_setattr
  xfs: XFS_IOCTL_SETXATTR can run in user namespaces
  xfs: kill xfs_ioctl_setattr behaviour mask
  xfs: disaggregate xfs_ioctl_setattr
  xfs: factor out xfs_ioctl_setattr transaciton preamble
  xfs: separate xflags from xfs_ioctl_setattr
  xfs: FSX_NONBLOCK is not used
  xfs: don't allocate an ioend for direct I/O completions
  xfs: change kmem_free to use generic kvfree()
  xfs: factor out a xfs_update_prealloc_flags() helper
  xfs: remove incorrect error negation in attr_multi ioctl
  xfs: set superblock buffer type correctly
  xfs: set buf types when converting extent formats
  ...
parents c5452a58 bad96266
...@@ -287,9 +287,9 @@ The following sysctls are available for the XFS filesystem: ...@@ -287,9 +287,9 @@ The following sysctls are available for the XFS filesystem:
XFS_ERRLEVEL_LOW: 1 XFS_ERRLEVEL_LOW: 1
XFS_ERRLEVEL_HIGH: 5 XFS_ERRLEVEL_HIGH: 5
fs.xfs.panic_mask (Min: 0 Default: 0 Max: 127) fs.xfs.panic_mask (Min: 0 Default: 0 Max: 255)
Causes certain error conditions to call BUG(). Value is a bitmask; Causes certain error conditions to call BUG(). Value is a bitmask;
AND together the tags which represent errors which should cause panics: OR together the tags which represent errors which should cause panics:
XFS_NO_PTAG 0 XFS_NO_PTAG 0
XFS_PTAG_IFLUSH 0x00000001 XFS_PTAG_IFLUSH 0x00000001
...@@ -299,6 +299,7 @@ The following sysctls are available for the XFS filesystem: ...@@ -299,6 +299,7 @@ The following sysctls are available for the XFS filesystem:
XFS_PTAG_SHUTDOWN_CORRUPT 0x00000010 XFS_PTAG_SHUTDOWN_CORRUPT 0x00000010
XFS_PTAG_SHUTDOWN_IOERROR 0x00000020 XFS_PTAG_SHUTDOWN_IOERROR 0x00000020
XFS_PTAG_SHUTDOWN_LOGERROR 0x00000040 XFS_PTAG_SHUTDOWN_LOGERROR 0x00000040
XFS_PTAG_FSBLOCK_ZERO 0x00000080
This option is intended for debugging only. This option is intended for debugging only.
...@@ -348,16 +349,13 @@ The following sysctls are available for the XFS filesystem: ...@@ -348,16 +349,13 @@ The following sysctls are available for the XFS filesystem:
Deprecated Sysctls Deprecated Sysctls
================== ==================
fs.xfs.xfsbufd_centisecs (Min: 50 Default: 100 Max: 3000) None at present.
Dirty metadata is now tracked by the log subsystem and
flushing is driven by log space and idling demands. The
xfsbufd no longer exists, so this syctl does nothing.
Due for removal in 3.14.
fs.xfs.age_buffer_centisecs (Min: 100 Default: 1500 Max: 720000) Removed Sysctls
Dirty metadata is now tracked by the log subsystem and ===============
flushing is driven by log space and idling demands. The
xfsbufd no longer exists, so this syctl does nothing.
Due for removal in 3.14. Name Removed
---- -------
fs.xfs.xfsbufd_centisec v3.20
fs.xfs.age_buffer_centisecs v3.20
...@@ -91,16 +91,6 @@ kmem_zalloc_large(size_t size, xfs_km_flags_t flags) ...@@ -91,16 +91,6 @@ kmem_zalloc_large(size_t size, xfs_km_flags_t flags)
return ptr; return ptr;
} }
void
kmem_free(const void *ptr)
{
if (!is_vmalloc_addr(ptr)) {
kfree(ptr);
} else {
vfree(ptr);
}
}
void * void *
kmem_realloc(const void *ptr, size_t newsize, size_t oldsize, kmem_realloc(const void *ptr, size_t newsize, size_t oldsize,
xfs_km_flags_t flags) xfs_km_flags_t flags)
......
...@@ -63,7 +63,10 @@ kmem_flags_convert(xfs_km_flags_t flags) ...@@ -63,7 +63,10 @@ kmem_flags_convert(xfs_km_flags_t flags)
extern void *kmem_alloc(size_t, xfs_km_flags_t); extern void *kmem_alloc(size_t, xfs_km_flags_t);
extern void *kmem_zalloc_large(size_t size, xfs_km_flags_t); extern void *kmem_zalloc_large(size_t size, xfs_km_flags_t);
extern void *kmem_realloc(const void *, size_t, size_t, xfs_km_flags_t); extern void *kmem_realloc(const void *, size_t, size_t, xfs_km_flags_t);
extern void kmem_free(const void *); static inline void kmem_free(const void *ptr)
{
kvfree(ptr);
}
extern void *kmem_zalloc_greedy(size_t *, size_t, size_t); extern void *kmem_zalloc_greedy(size_t *, size_t, size_t);
......
...@@ -403,7 +403,7 @@ xfs_sbversion_add_attr2(xfs_mount_t *mp, xfs_trans_t *tp) ...@@ -403,7 +403,7 @@ xfs_sbversion_add_attr2(xfs_mount_t *mp, xfs_trans_t *tp)
if (!xfs_sb_version_hasattr2(&mp->m_sb)) { if (!xfs_sb_version_hasattr2(&mp->m_sb)) {
xfs_sb_version_addattr2(&mp->m_sb); xfs_sb_version_addattr2(&mp->m_sb);
spin_unlock(&mp->m_sb_lock); spin_unlock(&mp->m_sb_lock);
xfs_mod_sb(tp, XFS_SB_VERSIONNUM | XFS_SB_FEATURES2); xfs_log_sb(tp);
} else } else
spin_unlock(&mp->m_sb_lock); spin_unlock(&mp->m_sb_lock);
} }
......
...@@ -973,7 +973,11 @@ xfs_bmap_local_to_extents( ...@@ -973,7 +973,11 @@ xfs_bmap_local_to_extents(
*firstblock = args.fsbno; *firstblock = args.fsbno;
bp = xfs_btree_get_bufl(args.mp, tp, args.fsbno, 0); bp = xfs_btree_get_bufl(args.mp, tp, args.fsbno, 0);
/* initialise the block and copy the data */ /*
* Initialise the block and copy the data
*
* Note: init_fn must set the buffer log item type correctly!
*/
init_fn(tp, bp, ip, ifp); init_fn(tp, bp, ip, ifp);
/* account for the change in fork size and log everything */ /* account for the change in fork size and log everything */
...@@ -1221,22 +1225,20 @@ xfs_bmap_add_attrfork( ...@@ -1221,22 +1225,20 @@ xfs_bmap_add_attrfork(
goto bmap_cancel; goto bmap_cancel;
if (!xfs_sb_version_hasattr(&mp->m_sb) || if (!xfs_sb_version_hasattr(&mp->m_sb) ||
(!xfs_sb_version_hasattr2(&mp->m_sb) && version == 2)) { (!xfs_sb_version_hasattr2(&mp->m_sb) && version == 2)) {
__int64_t sbfields = 0; bool log_sb = false;
spin_lock(&mp->m_sb_lock); spin_lock(&mp->m_sb_lock);
if (!xfs_sb_version_hasattr(&mp->m_sb)) { if (!xfs_sb_version_hasattr(&mp->m_sb)) {
xfs_sb_version_addattr(&mp->m_sb); xfs_sb_version_addattr(&mp->m_sb);
sbfields |= XFS_SB_VERSIONNUM; log_sb = true;
} }
if (!xfs_sb_version_hasattr2(&mp->m_sb) && version == 2) { if (!xfs_sb_version_hasattr2(&mp->m_sb) && version == 2) {
xfs_sb_version_addattr2(&mp->m_sb); xfs_sb_version_addattr2(&mp->m_sb);
sbfields |= (XFS_SB_VERSIONNUM | XFS_SB_FEATURES2); log_sb = true;
} }
if (sbfields) { spin_unlock(&mp->m_sb_lock);
spin_unlock(&mp->m_sb_lock); if (log_sb)
xfs_mod_sb(tp, sbfields); xfs_log_sb(tp);
} else
spin_unlock(&mp->m_sb_lock);
} }
error = xfs_bmap_finish(&tp, &flist, &committed); error = xfs_bmap_finish(&tp, &flist, &committed);
......
...@@ -27,6 +27,37 @@ struct xfs_trans; ...@@ -27,6 +27,37 @@ struct xfs_trans;
extern kmem_zone_t *xfs_bmap_free_item_zone; extern kmem_zone_t *xfs_bmap_free_item_zone;
/*
* Argument structure for xfs_bmap_alloc.
*/
struct xfs_bmalloca {
xfs_fsblock_t *firstblock; /* i/o first block allocated */
struct xfs_bmap_free *flist; /* bmap freelist */
struct xfs_trans *tp; /* transaction pointer */
struct xfs_inode *ip; /* incore inode pointer */
struct xfs_bmbt_irec prev; /* extent before the new one */
struct xfs_bmbt_irec got; /* extent after, or delayed */
xfs_fileoff_t offset; /* offset in file filling in */
xfs_extlen_t length; /* i/o length asked/allocated */
xfs_fsblock_t blkno; /* starting block of new extent */
struct xfs_btree_cur *cur; /* btree cursor */
xfs_extnum_t idx; /* current extent index */
int nallocs;/* number of extents alloc'd */
int logflags;/* flags for transaction logging */
xfs_extlen_t total; /* total blocks needed for xaction */
xfs_extlen_t minlen; /* minimum allocation size (blocks) */
xfs_extlen_t minleft; /* amount must be left after alloc */
bool eof; /* set if allocating past last extent */
bool wasdel; /* replacing a delayed allocation */
bool userdata;/* set if is user data */
bool aeof; /* allocated space at eof */
bool conv; /* overwriting unwritten extents */
int flags;
};
/* /*
* List of extents to be free "later". * List of extents to be free "later".
* The list is kept sorted on xbf_startblock. * The list is kept sorted on xbf_startblock.
...@@ -149,6 +180,8 @@ void xfs_bmap_local_to_extents_empty(struct xfs_inode *ip, int whichfork); ...@@ -149,6 +180,8 @@ void xfs_bmap_local_to_extents_empty(struct xfs_inode *ip, int whichfork);
void xfs_bmap_add_free(xfs_fsblock_t bno, xfs_filblks_t len, void xfs_bmap_add_free(xfs_fsblock_t bno, xfs_filblks_t len,
struct xfs_bmap_free *flist, struct xfs_mount *mp); struct xfs_bmap_free *flist, struct xfs_mount *mp);
void xfs_bmap_cancel(struct xfs_bmap_free *flist); void xfs_bmap_cancel(struct xfs_bmap_free *flist);
int xfs_bmap_finish(struct xfs_trans **tp, struct xfs_bmap_free *flist,
int *committed);
void xfs_bmap_compute_maxlevels(struct xfs_mount *mp, int whichfork); void xfs_bmap_compute_maxlevels(struct xfs_mount *mp, int whichfork);
int xfs_bmap_first_unused(struct xfs_trans *tp, struct xfs_inode *ip, int xfs_bmap_first_unused(struct xfs_trans *tp, struct xfs_inode *ip,
xfs_extlen_t len, xfs_fileoff_t *unused, int whichfork); xfs_extlen_t len, xfs_fileoff_t *unused, int whichfork);
......
...@@ -151,10 +151,13 @@ typedef struct xfs_sb { ...@@ -151,10 +151,13 @@ typedef struct xfs_sb {
__uint32_t sb_features2; /* additional feature bits */ __uint32_t sb_features2; /* additional feature bits */
/* /*
* bad features2 field as a result of failing to pad the sb * bad features2 field as a result of failing to pad the sb structure to
* structure to 64 bits. Some machines will be using this field * 64 bits. Some machines will be using this field for features2 bits.
* for features2 bits. Easiest just to mark it bad and not use * Easiest just to mark it bad and not use it for anything else.
* it for anything else. *
* This is not kept up to date in memory; it is always overwritten by
* the value in sb_features2 when formatting the incore superblock to
* the disk buffer.
*/ */
__uint32_t sb_bad_features2; __uint32_t sb_bad_features2;
...@@ -304,8 +307,8 @@ typedef enum { ...@@ -304,8 +307,8 @@ typedef enum {
#define XFS_SB_ICOUNT XFS_SB_MVAL(ICOUNT) #define XFS_SB_ICOUNT XFS_SB_MVAL(ICOUNT)
#define XFS_SB_IFREE XFS_SB_MVAL(IFREE) #define XFS_SB_IFREE XFS_SB_MVAL(IFREE)
#define XFS_SB_FDBLOCKS XFS_SB_MVAL(FDBLOCKS) #define XFS_SB_FDBLOCKS XFS_SB_MVAL(FDBLOCKS)
#define XFS_SB_FEATURES2 XFS_SB_MVAL(FEATURES2) #define XFS_SB_FEATURES2 (XFS_SB_MVAL(FEATURES2) | \
#define XFS_SB_BAD_FEATURES2 XFS_SB_MVAL(BAD_FEATURES2) XFS_SB_MVAL(BAD_FEATURES2))
#define XFS_SB_FEATURES_COMPAT XFS_SB_MVAL(FEATURES_COMPAT) #define XFS_SB_FEATURES_COMPAT XFS_SB_MVAL(FEATURES_COMPAT)
#define XFS_SB_FEATURES_RO_COMPAT XFS_SB_MVAL(FEATURES_RO_COMPAT) #define XFS_SB_FEATURES_RO_COMPAT XFS_SB_MVAL(FEATURES_RO_COMPAT)
#define XFS_SB_FEATURES_INCOMPAT XFS_SB_MVAL(FEATURES_INCOMPAT) #define XFS_SB_FEATURES_INCOMPAT XFS_SB_MVAL(FEATURES_INCOMPAT)
...@@ -319,9 +322,9 @@ typedef enum { ...@@ -319,9 +322,9 @@ typedef enum {
XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | \ XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | \
XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH | \ XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH | \
XFS_SB_ICOUNT | XFS_SB_IFREE | XFS_SB_FDBLOCKS | XFS_SB_FEATURES2 | \ XFS_SB_ICOUNT | XFS_SB_IFREE | XFS_SB_FDBLOCKS | XFS_SB_FEATURES2 | \
XFS_SB_BAD_FEATURES2 | XFS_SB_FEATURES_COMPAT | \ XFS_SB_FEATURES_COMPAT | XFS_SB_FEATURES_RO_COMPAT | \
XFS_SB_FEATURES_RO_COMPAT | XFS_SB_FEATURES_INCOMPAT | \ XFS_SB_FEATURES_INCOMPAT | XFS_SB_FEATURES_LOG_INCOMPAT | \
XFS_SB_FEATURES_LOG_INCOMPAT | XFS_SB_PQUOTINO) XFS_SB_PQUOTINO)
/* /*
...@@ -453,13 +456,11 @@ static inline void xfs_sb_version_addattr2(struct xfs_sb *sbp) ...@@ -453,13 +456,11 @@ static inline void xfs_sb_version_addattr2(struct xfs_sb *sbp)
{ {
sbp->sb_versionnum |= XFS_SB_VERSION_MOREBITSBIT; sbp->sb_versionnum |= XFS_SB_VERSION_MOREBITSBIT;
sbp->sb_features2 |= XFS_SB_VERSION2_ATTR2BIT; sbp->sb_features2 |= XFS_SB_VERSION2_ATTR2BIT;
sbp->sb_bad_features2 |= XFS_SB_VERSION2_ATTR2BIT;
} }
static inline void xfs_sb_version_removeattr2(struct xfs_sb *sbp) static inline void xfs_sb_version_removeattr2(struct xfs_sb *sbp)
{ {
sbp->sb_features2 &= ~XFS_SB_VERSION2_ATTR2BIT; sbp->sb_features2 &= ~XFS_SB_VERSION2_ATTR2BIT;
sbp->sb_bad_features2 &= ~XFS_SB_VERSION2_ATTR2BIT;
if (!sbp->sb_features2) if (!sbp->sb_features2)
sbp->sb_versionnum &= ~XFS_SB_VERSION_MOREBITSBIT; sbp->sb_versionnum &= ~XFS_SB_VERSION_MOREBITSBIT;
} }
...@@ -475,7 +476,6 @@ static inline void xfs_sb_version_addprojid32bit(struct xfs_sb *sbp) ...@@ -475,7 +476,6 @@ static inline void xfs_sb_version_addprojid32bit(struct xfs_sb *sbp)
{ {
sbp->sb_versionnum |= XFS_SB_VERSION_MOREBITSBIT; sbp->sb_versionnum |= XFS_SB_VERSION_MOREBITSBIT;
sbp->sb_features2 |= XFS_SB_VERSION2_PROJID32BIT; sbp->sb_features2 |= XFS_SB_VERSION2_PROJID32BIT;
sbp->sb_bad_features2 |= XFS_SB_VERSION2_PROJID32BIT;
} }
/* /*
......
...@@ -40,69 +40,6 @@ ...@@ -40,69 +40,6 @@
* Physical superblock buffer manipulations. Shared with libxfs in userspace. * Physical superblock buffer manipulations. Shared with libxfs in userspace.
*/ */
static const struct {
short offset;
short type; /* 0 = integer
* 1 = binary / string (no translation)
*/
} xfs_sb_info[] = {
{ offsetof(xfs_sb_t, sb_magicnum), 0 },
{ offsetof(xfs_sb_t, sb_blocksize), 0 },
{ offsetof(xfs_sb_t, sb_dblocks), 0 },
{ offsetof(xfs_sb_t, sb_rblocks), 0 },
{ offsetof(xfs_sb_t, sb_rextents), 0 },
{ offsetof(xfs_sb_t, sb_uuid), 1 },
{ offsetof(xfs_sb_t, sb_logstart), 0 },
{ offsetof(xfs_sb_t, sb_rootino), 0 },
{ offsetof(xfs_sb_t, sb_rbmino), 0 },
{ offsetof(xfs_sb_t, sb_rsumino), 0 },
{ offsetof(xfs_sb_t, sb_rextsize), 0 },
{ offsetof(xfs_sb_t, sb_agblocks), 0 },
{ offsetof(xfs_sb_t, sb_agcount), 0 },
{ offsetof(xfs_sb_t, sb_rbmblocks), 0 },
{ offsetof(xfs_sb_t, sb_logblocks), 0 },
{ offsetof(xfs_sb_t, sb_versionnum), 0 },
{ offsetof(xfs_sb_t, sb_sectsize), 0 },
{ offsetof(xfs_sb_t, sb_inodesize), 0 },
{ offsetof(xfs_sb_t, sb_inopblock), 0 },
{ offsetof(xfs_sb_t, sb_fname[0]), 1 },
{ offsetof(xfs_sb_t, sb_blocklog), 0 },
{ offsetof(xfs_sb_t, sb_sectlog), 0 },
{ offsetof(xfs_sb_t, sb_inodelog), 0 },
{ offsetof(xfs_sb_t, sb_inopblog), 0 },
{ offsetof(xfs_sb_t, sb_agblklog), 0 },
{ offsetof(xfs_sb_t, sb_rextslog), 0 },
{ offsetof(xfs_sb_t, sb_inprogress), 0 },
{ offsetof(xfs_sb_t, sb_imax_pct), 0 },
{ offsetof(xfs_sb_t, sb_icount), 0 },
{ offsetof(xfs_sb_t, sb_ifree), 0 },
{ offsetof(xfs_sb_t, sb_fdblocks), 0 },
{ offsetof(xfs_sb_t, sb_frextents), 0 },
{ offsetof(xfs_sb_t, sb_uquotino), 0 },
{ offsetof(xfs_sb_t, sb_gquotino), 0 },
{ offsetof(xfs_sb_t, sb_qflags), 0 },
{ offsetof(xfs_sb_t, sb_flags), 0 },
{ offsetof(xfs_sb_t, sb_shared_vn), 0 },
{ offsetof(xfs_sb_t, sb_inoalignmt), 0 },
{ offsetof(xfs_sb_t, sb_unit), 0 },
{ offsetof(xfs_sb_t, sb_width), 0 },
{ offsetof(xfs_sb_t, sb_dirblklog), 0 },
{ offsetof(xfs_sb_t, sb_logsectlog), 0 },
{ offsetof(xfs_sb_t, sb_logsectsize), 0 },
{ offsetof(xfs_sb_t, sb_logsunit), 0 },
{ offsetof(xfs_sb_t, sb_features2), 0 },
{ offsetof(xfs_sb_t, sb_bad_features2), 0 },
{ offsetof(xfs_sb_t, sb_features_compat), 0 },
{ offsetof(xfs_sb_t, sb_features_ro_compat), 0 },
{ offsetof(xfs_sb_t, sb_features_incompat), 0 },
{ offsetof(xfs_sb_t, sb_features_log_incompat), 0 },
{ offsetof(xfs_sb_t, sb_crc), 0 },
{ offsetof(xfs_sb_t, sb_pad), 0 },
{ offsetof(xfs_sb_t, sb_pquotino), 0 },
{ offsetof(xfs_sb_t, sb_lsn), 0 },
{ sizeof(xfs_sb_t), 0 }
};
/* /*
* Reference counting access wrappers to the perag structures. * Reference counting access wrappers to the perag structures.
* Because we never free per-ag structures, the only thing we * Because we never free per-ag structures, the only thing we
...@@ -461,58 +398,49 @@ xfs_sb_from_disk( ...@@ -461,58 +398,49 @@ xfs_sb_from_disk(
__xfs_sb_from_disk(to, from, true); __xfs_sb_from_disk(to, from, true);
} }
static inline void static void
xfs_sb_quota_to_disk( xfs_sb_quota_to_disk(
xfs_dsb_t *to, struct xfs_dsb *to,
xfs_sb_t *from, struct xfs_sb *from)
__int64_t *fields)
{ {
__uint16_t qflags = from->sb_qflags; __uint16_t qflags = from->sb_qflags;
to->sb_uquotino = cpu_to_be64(from->sb_uquotino);
if (xfs_sb_version_has_pquotino(from)) {
to->sb_qflags = cpu_to_be16(from->sb_qflags);
to->sb_gquotino = cpu_to_be64(from->sb_gquotino);
to->sb_pquotino = cpu_to_be64(from->sb_pquotino);
return;
}
/* /*
* We need to do these manipilations only if we are working * The in-core version of sb_qflags do not have XFS_OQUOTA_*
* with an older version of on-disk superblock. * flags, whereas the on-disk version does. So, convert incore
* XFS_{PG}QUOTA_* flags to on-disk XFS_OQUOTA_* flags.
*/ */
if (xfs_sb_version_has_pquotino(from)) qflags &= ~(XFS_PQUOTA_ENFD | XFS_PQUOTA_CHKD |
return; XFS_GQUOTA_ENFD | XFS_GQUOTA_CHKD);
if (*fields & XFS_SB_QFLAGS) { if (from->sb_qflags &
/* (XFS_PQUOTA_ENFD | XFS_GQUOTA_ENFD))
* The in-core version of sb_qflags do not have qflags |= XFS_OQUOTA_ENFD;
* XFS_OQUOTA_* flags, whereas the on-disk version if (from->sb_qflags &
* does. So, convert incore XFS_{PG}QUOTA_* flags (XFS_PQUOTA_CHKD | XFS_GQUOTA_CHKD))
* to on-disk XFS_OQUOTA_* flags. qflags |= XFS_OQUOTA_CHKD;
*/ to->sb_qflags = cpu_to_be16(qflags);
qflags &= ~(XFS_PQUOTA_ENFD | XFS_PQUOTA_CHKD |
XFS_GQUOTA_ENFD | XFS_GQUOTA_CHKD);
if (from->sb_qflags &
(XFS_PQUOTA_ENFD | XFS_GQUOTA_ENFD))
qflags |= XFS_OQUOTA_ENFD;
if (from->sb_qflags &
(XFS_PQUOTA_CHKD | XFS_GQUOTA_CHKD))
qflags |= XFS_OQUOTA_CHKD;
to->sb_qflags = cpu_to_be16(qflags);
*fields &= ~XFS_SB_QFLAGS;
}
/* /*
* GQUOTINO and PQUOTINO cannot be used together in versions of * GQUOTINO and PQUOTINO cannot be used together in versions
* superblock that do not have pquotino. from->sb_flags tells us which * of superblock that do not have pquotino. from->sb_flags
* quota is active and should be copied to disk. If neither are active, * tells us which quota is active and should be copied to
* make sure we write NULLFSINO to the sb_gquotino field as a quota * disk. If neither are active, we should NULL the inode.
* inode value of "0" is invalid when the XFS_SB_VERSION_QUOTA feature
* bit is set.
* *
* Note that we don't need to handle the sb_uquotino or sb_pquotino here * In all cases, the separate pquotino must remain 0 because it
* as they do not require any translation. Hence the main sb field loop * it beyond the "end" of the valid non-pquotino superblock.
* will write them appropriately from the in-core superblock.
*/ */
if ((*fields & XFS_SB_GQUOTINO) && if (from->sb_qflags & XFS_GQUOTA_ACCT)
(from->sb_qflags & XFS_GQUOTA_ACCT))
to->sb_gquotino = cpu_to_be64(from->sb_gquotino); to->sb_gquotino = cpu_to_be64(from->sb_gquotino);
else if ((*fields & XFS_SB_PQUOTINO) && else if (from->sb_qflags & XFS_PQUOTA_ACCT)
(from->sb_qflags & XFS_PQUOTA_ACCT))
to->sb_gquotino = cpu_to_be64(from->sb_pquotino); to->sb_gquotino = cpu_to_be64(from->sb_pquotino);
else { else {
/* /*
...@@ -526,63 +454,78 @@ xfs_sb_quota_to_disk( ...@@ -526,63 +454,78 @@ xfs_sb_quota_to_disk(
to->sb_gquotino = cpu_to_be64(NULLFSINO); to->sb_gquotino = cpu_to_be64(NULLFSINO);
} }
*fields &= ~(XFS_SB_PQUOTINO | XFS_SB_GQUOTINO); to->sb_pquotino = 0;
} }
/*
* Copy in core superblock to ondisk one.
*
* The fields argument is mask of superblock fields to copy.
*/
void void
xfs_sb_to_disk( xfs_sb_to_disk(
xfs_dsb_t *to, struct xfs_dsb *to,
xfs_sb_t *from, struct xfs_sb *from)
__int64_t fields)
{ {
xfs_caddr_t to_ptr = (xfs_caddr_t)to; xfs_sb_quota_to_disk(to, from);
xfs_caddr_t from_ptr = (xfs_caddr_t)from;
xfs_sb_field_t f;
int first;
int size;
ASSERT(fields);
if (!fields)
return;
/* We should never write the crc here, it's updated in the IO path */ to->sb_magicnum = cpu_to_be32(from->sb_magicnum);
fields &= ~XFS_SB_CRC; to->sb_blocksize = cpu_to_be32(from->sb_blocksize);
to->sb_dblocks = cpu_to_be64(from->sb_dblocks);
xfs_sb_quota_to_disk(to, from, &fields); to->sb_rblocks = cpu_to_be64(from->sb_rblocks);
while (fields) { to->sb_rextents = cpu_to_be64(from->sb_rextents);
f = (xfs_sb_field_t)xfs_lowbit64((__uint64_t)fields); memcpy(&to->sb_uuid, &from->sb_uuid, sizeof(to->sb_uuid));
first = xfs_sb_info[f].offset; to->sb_logstart = cpu_to_be64(from->sb_logstart);
size = xfs_sb_info[f + 1].offset - first; to->sb_rootino = cpu_to_be64(from->sb_rootino);
to->sb_rbmino = cpu_to_be64(from->sb_rbmino);
ASSERT(xfs_sb_info[f].type == 0 || xfs_sb_info[f].type == 1); to->sb_rsumino = cpu_to_be64(from->sb_rsumino);
to->sb_rextsize = cpu_to_be32(from->sb_rextsize);
if (size == 1 || xfs_sb_info[f].type == 1) { to->sb_agblocks = cpu_to_be32(from->sb_agblocks);
memcpy(to_ptr + first, from_ptr + first, size); to->sb_agcount = cpu_to_be32(from->sb_agcount);
} else { to->sb_rbmblocks = cpu_to_be32(from->sb_rbmblocks);
switch (size) { to->sb_logblocks = cpu_to_be32(from->sb_logblocks);
case 2: to->sb_versionnum = cpu_to_be16(from->sb_versionnum);
*(__be16 *)(to_ptr + first) = to->sb_sectsize = cpu_to_be16(from->sb_sectsize);
cpu_to_be16(*(__u16 *)(from_ptr + first)); to->sb_inodesize = cpu_to_be16(from->sb_inodesize);
break; to->sb_inopblock = cpu_to_be16(from->sb_inopblock);
case 4: memcpy(&to->sb_fname, &from->sb_fname, sizeof(to->sb_fname));
*(__be32 *)(to_ptr + first) = to->sb_blocklog = from->sb_blocklog;
cpu_to_be32(*(__u32 *)(from_ptr + first)); to->sb_sectlog = from->sb_sectlog;
break; to->sb_inodelog = from->sb_inodelog;
case 8: to->sb_inopblog = from->sb_inopblog;
*(__be64 *)(to_ptr + first) = to->sb_agblklog = from->sb_agblklog;
cpu_to_be64(*(__u64 *)(from_ptr + first)); to->sb_rextslog = from->sb_rextslog;
break; to->sb_inprogress = from->sb_inprogress;
default: to->sb_imax_pct = from->sb_imax_pct;
ASSERT(0); to->sb_icount = cpu_to_be64(from->sb_icount);
} to->sb_ifree = cpu_to_be64(from->sb_ifree);
} to->sb_fdblocks = cpu_to_be64(from->sb_fdblocks);
to->sb_frextents = cpu_to_be64(from->sb_frextents);
fields &= ~(1LL << f); to->sb_flags = from->sb_flags;
to->sb_shared_vn = from->sb_shared_vn;
to->sb_inoalignmt = cpu_to_be32(from->sb_inoalignmt);
to->sb_unit = cpu_to_be32(from->sb_unit);
to->sb_width = cpu_to_be32(from->sb_width);
to->sb_dirblklog = from->sb_dirblklog;
to->sb_logsectlog = from->sb_logsectlog;
to->sb_logsectsize = cpu_to_be16(from->sb_logsectsize);
to->sb_logsunit = cpu_to_be32(from->sb_logsunit);
/*
* We need to ensure that bad_features2 always matches features2.
* Hence we enforce that here rather than having to remember to do it
* everywhere else that updates features2.
*/
from->sb_bad_features2 = from->sb_features2;
to->sb_features2 = cpu_to_be32(from->sb_features2);
to->sb_bad_features2 = cpu_to_be32(from->sb_bad_features2);
if (xfs_sb_version_hascrc(from)) {
to->sb_features_compat = cpu_to_be32(from->sb_features_compat);
to->sb_features_ro_compat =
cpu_to_be32(from->sb_features_ro_compat);
to->sb_features_incompat =
cpu_to_be32(from->sb_features_incompat);
to->sb_features_log_incompat =
cpu_to_be32(from->sb_features_log_incompat);
to->sb_pad = 0;
to->sb_lsn = cpu_to_be64(from->sb_lsn);
} }
} }
...@@ -816,42 +759,51 @@ xfs_initialize_perag_data( ...@@ -816,42 +759,51 @@ xfs_initialize_perag_data(
} }
/* /*
* xfs_mod_sb() can be used to copy arbitrary changes to the * xfs_log_sb() can be used to copy arbitrary changes to the in-core superblock
* in-core superblock into the superblock buffer to be logged. * into the superblock buffer to be logged. It does not provide the higher
* It does not provide the higher level of locking that is * level of locking that is needed to protect the in-core superblock from
* needed to protect the in-core superblock from concurrent * concurrent access.
* access.
*/ */
void void
xfs_mod_sb(xfs_trans_t *tp, __int64_t fields) xfs_log_sb(
struct xfs_trans *tp)
{ {
xfs_buf_t *bp; struct xfs_mount *mp = tp->t_mountp;
int first; struct xfs_buf *bp = xfs_trans_getsb(tp, mp, 0);
int last;
xfs_mount_t *mp;
xfs_sb_field_t f;
ASSERT(fields);
if (!fields)
return;
mp = tp->t_mountp;
bp = xfs_trans_getsb(tp, mp, 0);
first = sizeof(xfs_sb_t);
last = 0;
/* translate/copy */
xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb, fields); xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb);
xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SB_BUF);
xfs_trans_log_buf(tp, bp, 0, sizeof(struct xfs_dsb));
}
/* find modified range */ /*
f = (xfs_sb_field_t)xfs_highbit64((__uint64_t)fields); * xfs_sync_sb
ASSERT((1LL << f) & XFS_SB_MOD_BITS); *
last = xfs_sb_info[f + 1].offset - 1; * Sync the superblock to disk.
*
* Note that the caller is responsible for checking the frozen state of the
* filesystem. This procedure uses the non-blocking transaction allocator and
* thus will allow modifications to a frozen fs. This is required because this
* code can be called during the process of freezing where use of the high-level
* allocator would deadlock.
*/
int
xfs_sync_sb(
struct xfs_mount *mp,
bool wait)
{
struct xfs_trans *tp;
int error;
f = (xfs_sb_field_t)xfs_lowbit64((__uint64_t)fields); tp = _xfs_trans_alloc(mp, XFS_TRANS_SB_CHANGE, KM_SLEEP);
ASSERT((1LL << f) & XFS_SB_MOD_BITS); error = xfs_trans_reserve(tp, &M_RES(mp)->tr_sb, 0, 0);
first = xfs_sb_info[f].offset; if (error) {
xfs_trans_cancel(tp, 0);
return error;
}
xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SB_BUF); xfs_log_sb(tp);
xfs_trans_log_buf(tp, bp, first, last); if (wait)
xfs_trans_set_sync(tp);
return xfs_trans_commit(tp, 0);
} }
...@@ -27,11 +27,12 @@ extern struct xfs_perag *xfs_perag_get_tag(struct xfs_mount *, xfs_agnumber_t, ...@@ -27,11 +27,12 @@ extern struct xfs_perag *xfs_perag_get_tag(struct xfs_mount *, xfs_agnumber_t,
extern void xfs_perag_put(struct xfs_perag *pag); extern void xfs_perag_put(struct xfs_perag *pag);
extern int xfs_initialize_perag_data(struct xfs_mount *, xfs_agnumber_t); extern int xfs_initialize_perag_data(struct xfs_mount *, xfs_agnumber_t);
extern void xfs_sb_calc_crc(struct xfs_buf *); extern void xfs_sb_calc_crc(struct xfs_buf *bp);
extern void xfs_mod_sb(struct xfs_trans *, __int64_t); extern void xfs_log_sb(struct xfs_trans *tp);
extern void xfs_sb_mount_common(struct xfs_mount *, struct xfs_sb *); extern int xfs_sync_sb(struct xfs_mount *mp, bool wait);
extern void xfs_sb_from_disk(struct xfs_sb *, struct xfs_dsb *); extern void xfs_sb_mount_common(struct xfs_mount *mp, struct xfs_sb *sbp);
extern void xfs_sb_to_disk(struct xfs_dsb *, struct xfs_sb *, __int64_t); extern void xfs_sb_from_disk(struct xfs_sb *to, struct xfs_dsb *from);
extern void xfs_sb_to_disk(struct xfs_dsb *to, struct xfs_sb *from);
extern void xfs_sb_quota_from_disk(struct xfs_sb *sbp); extern void xfs_sb_quota_from_disk(struct xfs_sb *sbp);
#endif /* __XFS_SB_H__ */ #endif /* __XFS_SB_H__ */
...@@ -82,7 +82,7 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops; ...@@ -82,7 +82,7 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops;
#define XFS_TRANS_ATTR_RM 23 #define XFS_TRANS_ATTR_RM 23
#define XFS_TRANS_ATTR_FLAG 24 #define XFS_TRANS_ATTR_FLAG 24
#define XFS_TRANS_CLEAR_AGI_BUCKET 25 #define XFS_TRANS_CLEAR_AGI_BUCKET 25
#define XFS_TRANS_QM_SBCHANGE 26 #define XFS_TRANS_SB_CHANGE 26
/* /*
* Dummy entries since we use the transaction type to index into the * Dummy entries since we use the transaction type to index into the
* trans_type[] in xlog_recover_print_trans_head() * trans_type[] in xlog_recover_print_trans_head()
...@@ -95,17 +95,15 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops; ...@@ -95,17 +95,15 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops;
#define XFS_TRANS_QM_DQCLUSTER 32 #define XFS_TRANS_QM_DQCLUSTER 32
#define XFS_TRANS_QM_QINOCREATE 33 #define XFS_TRANS_QM_QINOCREATE 33
#define XFS_TRANS_QM_QUOTAOFF_END 34 #define XFS_TRANS_QM_QUOTAOFF_END 34
#define XFS_TRANS_SB_UNIT 35 #define XFS_TRANS_FSYNC_TS 35
#define XFS_TRANS_FSYNC_TS 36 #define XFS_TRANS_GROWFSRT_ALLOC 36
#define XFS_TRANS_GROWFSRT_ALLOC 37 #define XFS_TRANS_GROWFSRT_ZERO 37
#define XFS_TRANS_GROWFSRT_ZERO 38 #define XFS_TRANS_GROWFSRT_FREE 38
#define XFS_TRANS_GROWFSRT_FREE 39 #define XFS_TRANS_SWAPEXT 39
#define XFS_TRANS_SWAPEXT 40 #define XFS_TRANS_CHECKPOINT 40
#define XFS_TRANS_SB_COUNT 41 #define XFS_TRANS_ICREATE 41
#define XFS_TRANS_CHECKPOINT 42 #define XFS_TRANS_CREATE_TMPFILE 42
#define XFS_TRANS_ICREATE 43 #define XFS_TRANS_TYPE_MAX 43
#define XFS_TRANS_CREATE_TMPFILE 44
#define XFS_TRANS_TYPE_MAX 44
/* new transaction types need to be reflected in xfs_logprint(8) */ /* new transaction types need to be reflected in xfs_logprint(8) */
#define XFS_TRANS_TYPES \ #define XFS_TRANS_TYPES \
...@@ -113,7 +111,6 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops; ...@@ -113,7 +111,6 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops;
{ XFS_TRANS_SETATTR_SIZE, "SETATTR_SIZE" }, \ { XFS_TRANS_SETATTR_SIZE, "SETATTR_SIZE" }, \
{ XFS_TRANS_INACTIVE, "INACTIVE" }, \ { XFS_TRANS_INACTIVE, "INACTIVE" }, \
{ XFS_TRANS_CREATE, "CREATE" }, \ { XFS_TRANS_CREATE, "CREATE" }, \
{ XFS_TRANS_CREATE_TMPFILE, "CREATE_TMPFILE" }, \
{ XFS_TRANS_CREATE_TRUNC, "CREATE_TRUNC" }, \ { XFS_TRANS_CREATE_TRUNC, "CREATE_TRUNC" }, \
{ XFS_TRANS_TRUNCATE_FILE, "TRUNCATE_FILE" }, \ { XFS_TRANS_TRUNCATE_FILE, "TRUNCATE_FILE" }, \
{ XFS_TRANS_REMOVE, "REMOVE" }, \ { XFS_TRANS_REMOVE, "REMOVE" }, \
...@@ -134,23 +131,23 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops; ...@@ -134,23 +131,23 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops;
{ XFS_TRANS_ATTR_RM, "ATTR_RM" }, \ { XFS_TRANS_ATTR_RM, "ATTR_RM" }, \
{ XFS_TRANS_ATTR_FLAG, "ATTR_FLAG" }, \ { XFS_TRANS_ATTR_FLAG, "ATTR_FLAG" }, \
{ XFS_TRANS_CLEAR_AGI_BUCKET, "CLEAR_AGI_BUCKET" }, \ { XFS_TRANS_CLEAR_AGI_BUCKET, "CLEAR_AGI_BUCKET" }, \
{ XFS_TRANS_QM_SBCHANGE, "QM_SBCHANGE" }, \ { XFS_TRANS_SB_CHANGE, "SBCHANGE" }, \
{ XFS_TRANS_DUMMY1, "DUMMY1" }, \
{ XFS_TRANS_DUMMY2, "DUMMY2" }, \
{ XFS_TRANS_QM_QUOTAOFF, "QM_QUOTAOFF" }, \ { XFS_TRANS_QM_QUOTAOFF, "QM_QUOTAOFF" }, \
{ XFS_TRANS_QM_DQALLOC, "QM_DQALLOC" }, \ { XFS_TRANS_QM_DQALLOC, "QM_DQALLOC" }, \
{ XFS_TRANS_QM_SETQLIM, "QM_SETQLIM" }, \ { XFS_TRANS_QM_SETQLIM, "QM_SETQLIM" }, \
{ XFS_TRANS_QM_DQCLUSTER, "QM_DQCLUSTER" }, \ { XFS_TRANS_QM_DQCLUSTER, "QM_DQCLUSTER" }, \
{ XFS_TRANS_QM_QINOCREATE, "QM_QINOCREATE" }, \ { XFS_TRANS_QM_QINOCREATE, "QM_QINOCREATE" }, \
{ XFS_TRANS_QM_QUOTAOFF_END, "QM_QOFF_END" }, \ { XFS_TRANS_QM_QUOTAOFF_END, "QM_QOFF_END" }, \
{ XFS_TRANS_SB_UNIT, "SB_UNIT" }, \
{ XFS_TRANS_FSYNC_TS, "FSYNC_TS" }, \ { XFS_TRANS_FSYNC_TS, "FSYNC_TS" }, \
{ XFS_TRANS_GROWFSRT_ALLOC, "GROWFSRT_ALLOC" }, \ { XFS_TRANS_GROWFSRT_ALLOC, "GROWFSRT_ALLOC" }, \
{ XFS_TRANS_GROWFSRT_ZERO, "GROWFSRT_ZERO" }, \ { XFS_TRANS_GROWFSRT_ZERO, "GROWFSRT_ZERO" }, \
{ XFS_TRANS_GROWFSRT_FREE, "GROWFSRT_FREE" }, \ { XFS_TRANS_GROWFSRT_FREE, "GROWFSRT_FREE" }, \
{ XFS_TRANS_SWAPEXT, "SWAPEXT" }, \ { XFS_TRANS_SWAPEXT, "SWAPEXT" }, \
{ XFS_TRANS_SB_COUNT, "SB_COUNT" }, \
{ XFS_TRANS_CHECKPOINT, "CHECKPOINT" }, \ { XFS_TRANS_CHECKPOINT, "CHECKPOINT" }, \
{ XFS_TRANS_DUMMY1, "DUMMY1" }, \ { XFS_TRANS_ICREATE, "ICREATE" }, \
{ XFS_TRANS_DUMMY2, "DUMMY2" }, \ { XFS_TRANS_CREATE_TMPFILE, "CREATE_TMPFILE" }, \
{ XLOG_UNMOUNT_REC_TYPE, "UNMOUNT" } { XLOG_UNMOUNT_REC_TYPE, "UNMOUNT" }
/* /*
......
...@@ -178,6 +178,8 @@ xfs_symlink_local_to_remote( ...@@ -178,6 +178,8 @@ xfs_symlink_local_to_remote(
struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount;
char *buf; char *buf;
xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SYMLINK_BUF);
if (!xfs_sb_version_hascrc(&mp->m_sb)) { if (!xfs_sb_version_hascrc(&mp->m_sb)) {
bp->b_ops = NULL; bp->b_ops = NULL;
memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes); memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes);
......
...@@ -715,17 +715,6 @@ xfs_calc_clear_agi_bucket_reservation( ...@@ -715,17 +715,6 @@ xfs_calc_clear_agi_bucket_reservation(
return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize); return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize);
} }
/*
* Clearing the quotaflags in the superblock.
* the super block for changing quota flags: sector size
*/
STATIC uint
xfs_calc_qm_sbchange_reservation(
struct xfs_mount *mp)
{
return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize);
}
/* /*
* Adjusting quota limits. * Adjusting quota limits.
* the xfs_disk_dquot_t: sizeof(struct xfs_disk_dquot) * the xfs_disk_dquot_t: sizeof(struct xfs_disk_dquot)
...@@ -864,9 +853,6 @@ xfs_trans_resv_calc( ...@@ -864,9 +853,6 @@ xfs_trans_resv_calc(
* The following transactions are logged in logical format with * The following transactions are logged in logical format with
* a default log count. * a default log count.
*/ */
resp->tr_qm_sbchange.tr_logres = xfs_calc_qm_sbchange_reservation(mp);
resp->tr_qm_sbchange.tr_logcount = XFS_DEFAULT_LOG_COUNT;
resp->tr_qm_setqlim.tr_logres = xfs_calc_qm_setqlim_reservation(mp); resp->tr_qm_setqlim.tr_logres = xfs_calc_qm_setqlim_reservation(mp);
resp->tr_qm_setqlim.tr_logcount = XFS_DEFAULT_LOG_COUNT; resp->tr_qm_setqlim.tr_logcount = XFS_DEFAULT_LOG_COUNT;
......
...@@ -56,7 +56,6 @@ struct xfs_trans_resv { ...@@ -56,7 +56,6 @@ struct xfs_trans_resv {
struct xfs_trans_res tr_growrtalloc; /* grow realtime allocations */ struct xfs_trans_res tr_growrtalloc; /* grow realtime allocations */
struct xfs_trans_res tr_growrtzero; /* grow realtime zeroing */ struct xfs_trans_res tr_growrtzero; /* grow realtime zeroing */
struct xfs_trans_res tr_growrtfree; /* grow realtime freeing */ struct xfs_trans_res tr_growrtfree; /* grow realtime freeing */
struct xfs_trans_res tr_qm_sbchange; /* change quota flags */
struct xfs_trans_res tr_qm_setqlim; /* adjust quota limits */ struct xfs_trans_res tr_qm_setqlim; /* adjust quota limits */
struct xfs_trans_res tr_qm_dqalloc; /* allocate quota on disk */ struct xfs_trans_res tr_qm_dqalloc; /* allocate quota on disk */
struct xfs_trans_res tr_qm_quotaoff; /* turn quota off */ struct xfs_trans_res tr_qm_quotaoff; /* turn quota off */
......
...@@ -135,30 +135,22 @@ xfs_setfilesize_trans_alloc( ...@@ -135,30 +135,22 @@ xfs_setfilesize_trans_alloc(
*/ */
STATIC int STATIC int
xfs_setfilesize( xfs_setfilesize(
struct xfs_ioend *ioend) struct xfs_inode *ip,
struct xfs_trans *tp,
xfs_off_t offset,
size_t size)
{ {
struct xfs_inode *ip = XFS_I(ioend->io_inode);
struct xfs_trans *tp = ioend->io_append_trans;
xfs_fsize_t isize; xfs_fsize_t isize;
/*
* The transaction may have been allocated in the I/O submission thread,
* thus we need to mark ourselves as beeing in a transaction manually.
* Similarly for freeze protection.
*/
current_set_flags_nested(&tp->t_pflags, PF_FSTRANS);
rwsem_acquire_read(&VFS_I(ip)->i_sb->s_writers.lock_map[SB_FREEZE_FS-1],
0, 1, _THIS_IP_);
xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_ilock(ip, XFS_ILOCK_EXCL);
isize = xfs_new_eof(ip, ioend->io_offset + ioend->io_size); isize = xfs_new_eof(ip, offset + size);
if (!isize) { if (!isize) {
xfs_iunlock(ip, XFS_ILOCK_EXCL); xfs_iunlock(ip, XFS_ILOCK_EXCL);
xfs_trans_cancel(tp, 0); xfs_trans_cancel(tp, 0);
return 0; return 0;
} }
trace_xfs_setfilesize(ip, ioend->io_offset, ioend->io_size); trace_xfs_setfilesize(ip, offset, size);
ip->i_d.di_size = isize; ip->i_d.di_size = isize;
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
...@@ -167,6 +159,25 @@ xfs_setfilesize( ...@@ -167,6 +159,25 @@ xfs_setfilesize(
return xfs_trans_commit(tp, 0); return xfs_trans_commit(tp, 0);
} }
STATIC int
xfs_setfilesize_ioend(
struct xfs_ioend *ioend)
{
struct xfs_inode *ip = XFS_I(ioend->io_inode);
struct xfs_trans *tp = ioend->io_append_trans;
/*
* The transaction may have been allocated in the I/O submission thread,
* thus we need to mark ourselves as being in a transaction manually.
* Similarly for freeze protection.
*/
current_set_flags_nested(&tp->t_pflags, PF_FSTRANS);
rwsem_acquire_read(&VFS_I(ip)->i_sb->s_writers.lock_map[SB_FREEZE_FS-1],
0, 1, _THIS_IP_);
return xfs_setfilesize(ip, tp, ioend->io_offset, ioend->io_size);
}
/* /*
* Schedule IO completion handling on the final put of an ioend. * Schedule IO completion handling on the final put of an ioend.
* *
...@@ -182,8 +193,7 @@ xfs_finish_ioend( ...@@ -182,8 +193,7 @@ xfs_finish_ioend(
if (ioend->io_type == XFS_IO_UNWRITTEN) if (ioend->io_type == XFS_IO_UNWRITTEN)
queue_work(mp->m_unwritten_workqueue, &ioend->io_work); queue_work(mp->m_unwritten_workqueue, &ioend->io_work);
else if (ioend->io_append_trans || else if (ioend->io_append_trans)
(ioend->io_isdirect && xfs_ioend_is_append(ioend)))
queue_work(mp->m_data_workqueue, &ioend->io_work); queue_work(mp->m_data_workqueue, &ioend->io_work);
else else
xfs_destroy_ioend(ioend); xfs_destroy_ioend(ioend);
...@@ -215,22 +225,8 @@ xfs_end_io( ...@@ -215,22 +225,8 @@ xfs_end_io(
if (ioend->io_type == XFS_IO_UNWRITTEN) { if (ioend->io_type == XFS_IO_UNWRITTEN) {
error = xfs_iomap_write_unwritten(ip, ioend->io_offset, error = xfs_iomap_write_unwritten(ip, ioend->io_offset,
ioend->io_size); ioend->io_size);
} else if (ioend->io_isdirect && xfs_ioend_is_append(ioend)) {
/*
* For direct I/O we do not know if we need to allocate blocks
* or not so we can't preallocate an append transaction as that
* results in nested reservations and log space deadlocks. Hence
* allocate the transaction here. While this is sub-optimal and
* can block IO completion for some time, we're stuck with doing
* it this way until we can pass the ioend to the direct IO
* allocation callbacks and avoid nesting that way.
*/
error = xfs_setfilesize_trans_alloc(ioend);
if (error)
goto done;
error = xfs_setfilesize(ioend);
} else if (ioend->io_append_trans) { } else if (ioend->io_append_trans) {
error = xfs_setfilesize(ioend); error = xfs_setfilesize_ioend(ioend);
} else { } else {
ASSERT(!xfs_ioend_is_append(ioend)); ASSERT(!xfs_ioend_is_append(ioend));
} }
...@@ -241,17 +237,6 @@ xfs_end_io( ...@@ -241,17 +237,6 @@ xfs_end_io(
xfs_destroy_ioend(ioend); xfs_destroy_ioend(ioend);
} }
/*
* Call IO completion handling in caller context on the final put of an ioend.
*/
STATIC void
xfs_finish_ioend_sync(
struct xfs_ioend *ioend)
{
if (atomic_dec_and_test(&ioend->io_remaining))
xfs_end_io(&ioend->io_work);
}
/* /*
* Allocate and initialise an IO completion structure. * Allocate and initialise an IO completion structure.
* We need to track unwritten extent write completion here initially. * We need to track unwritten extent write completion here initially.
...@@ -273,7 +258,6 @@ xfs_alloc_ioend( ...@@ -273,7 +258,6 @@ xfs_alloc_ioend(
* all the I/O from calling the completion routine too early. * all the I/O from calling the completion routine too early.
*/ */
atomic_set(&ioend->io_remaining, 1); atomic_set(&ioend->io_remaining, 1);
ioend->io_isdirect = 0;
ioend->io_error = 0; ioend->io_error = 0;
ioend->io_list = NULL; ioend->io_list = NULL;
ioend->io_type = type; ioend->io_type = type;
...@@ -1459,11 +1443,7 @@ xfs_get_blocks_direct( ...@@ -1459,11 +1443,7 @@ xfs_get_blocks_direct(
* *
* If the private argument is non-NULL __xfs_get_blocks signals us that we * If the private argument is non-NULL __xfs_get_blocks signals us that we
* need to issue a transaction to convert the range from unwritten to written * need to issue a transaction to convert the range from unwritten to written
* extents. In case this is regular synchronous I/O we just call xfs_end_io * extents.
* to do this and we are done. But in case this was a successful AIO
* request this handler is called from interrupt context, from which we
* can't start transactions. In that case offload the I/O completion to
* the workqueues we also use for buffered I/O completion.
*/ */
STATIC void STATIC void
xfs_end_io_direct_write( xfs_end_io_direct_write(
...@@ -1472,7 +1452,12 @@ xfs_end_io_direct_write( ...@@ -1472,7 +1452,12 @@ xfs_end_io_direct_write(
ssize_t size, ssize_t size,
void *private) void *private)
{ {
struct xfs_ioend *ioend = iocb->private; struct inode *inode = file_inode(iocb->ki_filp);
struct xfs_inode *ip = XFS_I(inode);
struct xfs_mount *mp = ip->i_mount;
if (XFS_FORCED_SHUTDOWN(mp))
return;
/* /*
* While the generic direct I/O code updates the inode size, it does * While the generic direct I/O code updates the inode size, it does
...@@ -1480,22 +1465,33 @@ xfs_end_io_direct_write( ...@@ -1480,22 +1465,33 @@ xfs_end_io_direct_write(
* end_io handler thinks the on-disk size is outside the in-core * end_io handler thinks the on-disk size is outside the in-core
* size. To prevent this just update it a little bit earlier here. * size. To prevent this just update it a little bit earlier here.
*/ */
if (offset + size > i_size_read(ioend->io_inode)) if (offset + size > i_size_read(inode))
i_size_write(ioend->io_inode, offset + size); i_size_write(inode, offset + size);
/* /*
* blockdev_direct_IO can return an error even after the I/O * For direct I/O we do not know if we need to allocate blocks or not,
* completion handler was called. Thus we need to protect * so we can't preallocate an append transaction, as that results in
* against double-freeing. * nested reservations and log space deadlocks. Hence allocate the
* transaction here. While this is sub-optimal and can block IO
* completion for some time, we're stuck with doing it this way until
* we can pass the ioend to the direct IO allocation callbacks and
* avoid nesting that way.
*/ */
iocb->private = NULL; if (private && size > 0) {
xfs_iomap_write_unwritten(ip, offset, size);
ioend->io_offset = offset; } else if (offset + size > ip->i_d.di_size) {
ioend->io_size = size; struct xfs_trans *tp;
if (private && size > 0) int error;
ioend->io_type = XFS_IO_UNWRITTEN;
tp = xfs_trans_alloc(mp, XFS_TRANS_FSYNC_TS);
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_fsyncts, 0, 0);
if (error) {
xfs_trans_cancel(tp, 0);
return;
}
xfs_finish_ioend_sync(ioend); xfs_setfilesize(ip, tp, offset, size);
}
} }
STATIC ssize_t STATIC ssize_t
...@@ -1507,39 +1503,16 @@ xfs_vm_direct_IO( ...@@ -1507,39 +1503,16 @@ xfs_vm_direct_IO(
{ {
struct inode *inode = iocb->ki_filp->f_mapping->host; struct inode *inode = iocb->ki_filp->f_mapping->host;
struct block_device *bdev = xfs_find_bdev_for_inode(inode); struct block_device *bdev = xfs_find_bdev_for_inode(inode);
struct xfs_ioend *ioend = NULL;
ssize_t ret;
if (rw & WRITE) { if (rw & WRITE) {
size_t size = iov_iter_count(iter); return __blockdev_direct_IO(rw, iocb, inode, bdev, iter,
/*
* We cannot preallocate a size update transaction here as we
* don't know whether allocation is necessary or not. Hence we
* can only tell IO completion that one is necessary if we are
* not doing unwritten extent conversion.
*/
iocb->private = ioend = xfs_alloc_ioend(inode, XFS_IO_DIRECT);
if (offset + size > XFS_I(inode)->i_d.di_size)
ioend->io_isdirect = 1;
ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iter,
offset, xfs_get_blocks_direct, offset, xfs_get_blocks_direct,
xfs_end_io_direct_write, NULL, xfs_end_io_direct_write, NULL,
DIO_ASYNC_EXTEND); DIO_ASYNC_EXTEND);
if (ret != -EIOCBQUEUED && iocb->private)
goto out_destroy_ioend;
} else {
ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iter,
offset, xfs_get_blocks_direct,
NULL, NULL, 0);
} }
return __blockdev_direct_IO(rw, iocb, inode, bdev, iter,
return ret; offset, xfs_get_blocks_direct,
NULL, NULL, 0);
out_destroy_ioend:
xfs_destroy_ioend(ioend);
return ret;
} }
/* /*
......
...@@ -24,14 +24,12 @@ extern mempool_t *xfs_ioend_pool; ...@@ -24,14 +24,12 @@ extern mempool_t *xfs_ioend_pool;
* Types of I/O for bmap clustering and I/O completion tracking. * Types of I/O for bmap clustering and I/O completion tracking.
*/ */
enum { enum {
XFS_IO_DIRECT = 0, /* special case for direct I/O ioends */
XFS_IO_DELALLOC, /* covers delalloc region */ XFS_IO_DELALLOC, /* covers delalloc region */
XFS_IO_UNWRITTEN, /* covers allocated but uninitialized data */ XFS_IO_UNWRITTEN, /* covers allocated but uninitialized data */
XFS_IO_OVERWRITE, /* covers already allocated extent */ XFS_IO_OVERWRITE, /* covers already allocated extent */
}; };
#define XFS_IO_TYPES \ #define XFS_IO_TYPES \
{ 0, "" }, \
{ XFS_IO_DELALLOC, "delalloc" }, \ { XFS_IO_DELALLOC, "delalloc" }, \
{ XFS_IO_UNWRITTEN, "unwritten" }, \ { XFS_IO_UNWRITTEN, "unwritten" }, \
{ XFS_IO_OVERWRITE, "overwrite" } { XFS_IO_OVERWRITE, "overwrite" }
...@@ -45,7 +43,6 @@ typedef struct xfs_ioend { ...@@ -45,7 +43,6 @@ typedef struct xfs_ioend {
unsigned int io_type; /* delalloc / unwritten */ unsigned int io_type; /* delalloc / unwritten */
int io_error; /* I/O error code */ int io_error; /* I/O error code */
atomic_t io_remaining; /* hold count */ atomic_t io_remaining; /* hold count */
unsigned int io_isdirect : 1;/* direct I/O */
struct inode *io_inode; /* file being written to */ struct inode *io_inode; /* file being written to */
struct buffer_head *io_buffer_head;/* buffer linked list head */ struct buffer_head *io_buffer_head;/* buffer linked list head */
struct buffer_head *io_buffer_tail;/* buffer linked list tail */ struct buffer_head *io_buffer_tail;/* buffer linked list tail */
......
...@@ -26,43 +26,8 @@ struct xfs_ifork; ...@@ -26,43 +26,8 @@ struct xfs_ifork;
struct xfs_inode; struct xfs_inode;
struct xfs_mount; struct xfs_mount;
struct xfs_trans; struct xfs_trans;
struct xfs_bmalloca;
/*
* Argument structure for xfs_bmap_alloc.
*/
struct xfs_bmalloca {
xfs_fsblock_t *firstblock; /* i/o first block allocated */
struct xfs_bmap_free *flist; /* bmap freelist */
struct xfs_trans *tp; /* transaction pointer */
struct xfs_inode *ip; /* incore inode pointer */
struct xfs_bmbt_irec prev; /* extent before the new one */
struct xfs_bmbt_irec got; /* extent after, or delayed */
xfs_fileoff_t offset; /* offset in file filling in */
xfs_extlen_t length; /* i/o length asked/allocated */
xfs_fsblock_t blkno; /* starting block of new extent */
struct xfs_btree_cur *cur; /* btree cursor */
xfs_extnum_t idx; /* current extent index */
int nallocs;/* number of extents alloc'd */
int logflags;/* flags for transaction logging */
xfs_extlen_t total; /* total blocks needed for xaction */
xfs_extlen_t minlen; /* minimum allocation size (blocks) */
xfs_extlen_t minleft; /* amount must be left after alloc */
bool eof; /* set if allocating past last extent */
bool wasdel; /* replacing a delayed allocation */
bool userdata;/* set if is user data */
bool aeof; /* allocated space at eof */
bool conv; /* overwriting unwritten extents */
int flags;
struct completion *done;
struct work_struct work;
int result;
};
int xfs_bmap_finish(struct xfs_trans **tp, struct xfs_bmap_free *flist,
int *committed);
int xfs_bmap_rtalloc(struct xfs_bmalloca *ap); int xfs_bmap_rtalloc(struct xfs_bmalloca *ap);
int xfs_bmap_eof(struct xfs_inode *ip, xfs_fileoff_t endoff, int xfs_bmap_eof(struct xfs_inode *ip, xfs_fileoff_t endoff,
int whichfork, int *eof); int whichfork, int *eof);
......
...@@ -319,6 +319,10 @@ xfs_buf_item_format( ...@@ -319,6 +319,10 @@ xfs_buf_item_format(
ASSERT(atomic_read(&bip->bli_refcount) > 0); ASSERT(atomic_read(&bip->bli_refcount) > 0);
ASSERT((bip->bli_flags & XFS_BLI_LOGGED) || ASSERT((bip->bli_flags & XFS_BLI_LOGGED) ||
(bip->bli_flags & XFS_BLI_STALE)); (bip->bli_flags & XFS_BLI_STALE));
ASSERT((bip->bli_flags & XFS_BLI_STALE) ||
(xfs_blft_from_flags(&bip->__bli_format) > XFS_BLFT_UNKNOWN_BUF
&& xfs_blft_from_flags(&bip->__bli_format) < XFS_BLFT_MAX_BUF));
/* /*
* If it is an inode buffer, transfer the in-memory state to the * If it is an inode buffer, transfer the in-memory state to the
...@@ -535,7 +539,7 @@ xfs_buf_item_push( ...@@ -535,7 +539,7 @@ xfs_buf_item_push(
if ((bp->b_flags & XBF_WRITE_FAIL) && if ((bp->b_flags & XBF_WRITE_FAIL) &&
___ratelimit(&xfs_buf_write_fail_rl_state, "XFS:")) { ___ratelimit(&xfs_buf_write_fail_rl_state, "XFS:")) {
xfs_warn(bp->b_target->bt_mount, xfs_warn(bp->b_target->bt_mount,
"Detected failing async write on buffer block 0x%llx. Retrying async write.\n", "Detected failing async write on buffer block 0x%llx. Retrying async write.",
(long long)bp->b_bn); (long long)bp->b_bn);
} }
......
...@@ -86,7 +86,7 @@ static inline void xfs_dqflock(xfs_dquot_t *dqp) ...@@ -86,7 +86,7 @@ static inline void xfs_dqflock(xfs_dquot_t *dqp)
wait_for_completion(&dqp->q_flush); wait_for_completion(&dqp->q_flush);
} }
static inline int xfs_dqflock_nowait(xfs_dquot_t *dqp) static inline bool xfs_dqflock_nowait(xfs_dquot_t *dqp)
{ {
return try_wait_for_completion(&dqp->q_flush); return try_wait_for_completion(&dqp->q_flush);
} }
......
...@@ -127,6 +127,42 @@ xfs_iozero( ...@@ -127,6 +127,42 @@ xfs_iozero(
return (-status); return (-status);
} }
int
xfs_update_prealloc_flags(
struct xfs_inode *ip,
enum xfs_prealloc_flags flags)
{
struct xfs_trans *tp;
int error;
tp = xfs_trans_alloc(ip->i_mount, XFS_TRANS_WRITEID);
error = xfs_trans_reserve(tp, &M_RES(ip->i_mount)->tr_writeid, 0, 0);
if (error) {
xfs_trans_cancel(tp, 0);
return error;
}
xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
if (!(flags & XFS_PREALLOC_INVISIBLE)) {
ip->i_d.di_mode &= ~S_ISUID;
if (ip->i_d.di_mode & S_IXGRP)
ip->i_d.di_mode &= ~S_ISGID;
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
}
if (flags & XFS_PREALLOC_SET)
ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC;
if (flags & XFS_PREALLOC_CLEAR)
ip->i_d.di_flags &= ~XFS_DIFLAG_PREALLOC;
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
if (flags & XFS_PREALLOC_SYNC)
xfs_trans_set_sync(tp);
return xfs_trans_commit(tp, 0);
}
/* /*
* Fsync operations on directories are much simpler than on regular files, * Fsync operations on directories are much simpler than on regular files,
* as there is no file data to flush, and thus also no need for explicit * as there is no file data to flush, and thus also no need for explicit
...@@ -784,8 +820,8 @@ xfs_file_fallocate( ...@@ -784,8 +820,8 @@ xfs_file_fallocate(
{ {
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
struct xfs_inode *ip = XFS_I(inode); struct xfs_inode *ip = XFS_I(inode);
struct xfs_trans *tp;
long error; long error;
enum xfs_prealloc_flags flags = 0;
loff_t new_size = 0; loff_t new_size = 0;
if (!S_ISREG(inode->i_mode)) if (!S_ISREG(inode->i_mode))
...@@ -822,6 +858,8 @@ xfs_file_fallocate( ...@@ -822,6 +858,8 @@ xfs_file_fallocate(
if (error) if (error)
goto out_unlock; goto out_unlock;
} else { } else {
flags |= XFS_PREALLOC_SET;
if (!(mode & FALLOC_FL_KEEP_SIZE) && if (!(mode & FALLOC_FL_KEEP_SIZE) &&
offset + len > i_size_read(inode)) { offset + len > i_size_read(inode)) {
new_size = offset + len; new_size = offset + len;
...@@ -839,28 +877,10 @@ xfs_file_fallocate( ...@@ -839,28 +877,10 @@ xfs_file_fallocate(
goto out_unlock; goto out_unlock;
} }
tp = xfs_trans_alloc(ip->i_mount, XFS_TRANS_WRITEID);
error = xfs_trans_reserve(tp, &M_RES(ip->i_mount)->tr_writeid, 0, 0);
if (error) {
xfs_trans_cancel(tp, 0);
goto out_unlock;
}
xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
ip->i_d.di_mode &= ~S_ISUID;
if (ip->i_d.di_mode & S_IXGRP)
ip->i_d.di_mode &= ~S_ISGID;
if (!(mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE)))
ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC;
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
if (file->f_flags & O_DSYNC) if (file->f_flags & O_DSYNC)
xfs_trans_set_sync(tp); flags |= XFS_PREALLOC_SYNC;
error = xfs_trans_commit(tp, 0);
error = xfs_update_prealloc_flags(ip, flags);
if (error) if (error)
goto out_unlock; goto out_unlock;
......
...@@ -488,6 +488,7 @@ xfs_growfs_data_private( ...@@ -488,6 +488,7 @@ xfs_growfs_data_private(
xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, nfree); xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, nfree);
if (dpct) if (dpct)
xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct); xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct);
xfs_trans_set_sync(tp);
error = xfs_trans_commit(tp, 0); error = xfs_trans_commit(tp, 0);
if (error) if (error)
return error; return error;
...@@ -541,7 +542,7 @@ xfs_growfs_data_private( ...@@ -541,7 +542,7 @@ xfs_growfs_data_private(
saved_error = error; saved_error = error;
continue; continue;
} }
xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb, XFS_SB_ALL_BITS); xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb);
error = xfs_bwrite(bp); error = xfs_bwrite(bp);
xfs_buf_relse(bp); xfs_buf_relse(bp);
...@@ -756,37 +757,6 @@ xfs_reserve_blocks( ...@@ -756,37 +757,6 @@ xfs_reserve_blocks(
return 0; return 0;
} }
/*
* Dump a transaction into the log that contains no real change. This is needed
* to be able to make the log dirty or stamp the current tail LSN into the log
* during the covering operation.
*
* We cannot use an inode here for this - that will push dirty state back up
* into the VFS and then periodic inode flushing will prevent log covering from
* making progress. Hence we log a field in the superblock instead and use a
* synchronous transaction to ensure the superblock is immediately unpinned
* and can be written back.
*/
int
xfs_fs_log_dummy(
xfs_mount_t *mp)
{
xfs_trans_t *tp;
int error;
tp = _xfs_trans_alloc(mp, XFS_TRANS_DUMMY1, KM_SLEEP);
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_sb, 0, 0);
if (error) {
xfs_trans_cancel(tp, 0);
return error;
}
/* log the UUID because it is an unchanging field */
xfs_mod_sb(tp, XFS_SB_UUID);
xfs_trans_set_sync(tp);
return xfs_trans_commit(tp, 0);
}
int int
xfs_fs_goingdown( xfs_fs_goingdown(
xfs_mount_t *mp, xfs_mount_t *mp,
......
...@@ -1995,6 +1995,7 @@ xfs_iunlink( ...@@ -1995,6 +1995,7 @@ xfs_iunlink(
agi->agi_unlinked[bucket_index] = cpu_to_be32(agino); agi->agi_unlinked[bucket_index] = cpu_to_be32(agino);
offset = offsetof(xfs_agi_t, agi_unlinked) + offset = offsetof(xfs_agi_t, agi_unlinked) +
(sizeof(xfs_agino_t) * bucket_index); (sizeof(xfs_agino_t) * bucket_index);
xfs_trans_buf_set_type(tp, agibp, XFS_BLFT_AGI_BUF);
xfs_trans_log_buf(tp, agibp, offset, xfs_trans_log_buf(tp, agibp, offset,
(offset + sizeof(xfs_agino_t) - 1)); (offset + sizeof(xfs_agino_t) - 1));
return 0; return 0;
...@@ -2086,6 +2087,7 @@ xfs_iunlink_remove( ...@@ -2086,6 +2087,7 @@ xfs_iunlink_remove(
agi->agi_unlinked[bucket_index] = cpu_to_be32(next_agino); agi->agi_unlinked[bucket_index] = cpu_to_be32(next_agino);
offset = offsetof(xfs_agi_t, agi_unlinked) + offset = offsetof(xfs_agi_t, agi_unlinked) +
(sizeof(xfs_agino_t) * bucket_index); (sizeof(xfs_agino_t) * bucket_index);
xfs_trans_buf_set_type(tp, agibp, XFS_BLFT_AGI_BUF);
xfs_trans_log_buf(tp, agibp, offset, xfs_trans_log_buf(tp, agibp, offset,
(offset + sizeof(xfs_agino_t) - 1)); (offset + sizeof(xfs_agino_t) - 1));
} else { } else {
...@@ -2655,6 +2657,124 @@ xfs_sort_for_rename( ...@@ -2655,6 +2657,124 @@ xfs_sort_for_rename(
} }
} }
/*
* xfs_cross_rename()
*
* responsible for handling RENAME_EXCHANGE flag in renameat2() sytemcall
*/
STATIC int
xfs_cross_rename(
struct xfs_trans *tp,
struct xfs_inode *dp1,
struct xfs_name *name1,
struct xfs_inode *ip1,
struct xfs_inode *dp2,
struct xfs_name *name2,
struct xfs_inode *ip2,
struct xfs_bmap_free *free_list,
xfs_fsblock_t *first_block,
int spaceres)
{
int error = 0;
int ip1_flags = 0;
int ip2_flags = 0;
int dp2_flags = 0;
/* Swap inode number for dirent in first parent */
error = xfs_dir_replace(tp, dp1, name1,
ip2->i_ino,
first_block, free_list, spaceres);
if (error)
goto out;
/* Swap inode number for dirent in second parent */
error = xfs_dir_replace(tp, dp2, name2,
ip1->i_ino,
first_block, free_list, spaceres);
if (error)
goto out;
/*
* If we're renaming one or more directories across different parents,
* update the respective ".." entries (and link counts) to match the new
* parents.
*/
if (dp1 != dp2) {
dp2_flags = XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG;
if (S_ISDIR(ip2->i_d.di_mode)) {
error = xfs_dir_replace(tp, ip2, &xfs_name_dotdot,
dp1->i_ino, first_block,
free_list, spaceres);
if (error)
goto out;
/* transfer ip2 ".." reference to dp1 */
if (!S_ISDIR(ip1->i_d.di_mode)) {
error = xfs_droplink(tp, dp2);
if (error)
goto out;
error = xfs_bumplink(tp, dp1);
if (error)
goto out;
}
/*
* Although ip1 isn't changed here, userspace needs
* to be warned about the change, so that applications
* relying on it (like backup ones), will properly
* notify the change
*/
ip1_flags |= XFS_ICHGTIME_CHG;
ip2_flags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG;
}
if (S_ISDIR(ip1->i_d.di_mode)) {
error = xfs_dir_replace(tp, ip1, &xfs_name_dotdot,
dp2->i_ino, first_block,
free_list, spaceres);
if (error)
goto out;
/* transfer ip1 ".." reference to dp2 */
if (!S_ISDIR(ip2->i_d.di_mode)) {
error = xfs_droplink(tp, dp1);
if (error)
goto out;
error = xfs_bumplink(tp, dp2);
if (error)
goto out;
}
/*
* Although ip2 isn't changed here, userspace needs
* to be warned about the change, so that applications
* relying on it (like backup ones), will properly
* notify the change
*/
ip1_flags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG;
ip2_flags |= XFS_ICHGTIME_CHG;
}
}
if (ip1_flags) {
xfs_trans_ichgtime(tp, ip1, ip1_flags);
xfs_trans_log_inode(tp, ip1, XFS_ILOG_CORE);
}
if (ip2_flags) {
xfs_trans_ichgtime(tp, ip2, ip2_flags);
xfs_trans_log_inode(tp, ip2, XFS_ILOG_CORE);
}
if (dp2_flags) {
xfs_trans_ichgtime(tp, dp2, dp2_flags);
xfs_trans_log_inode(tp, dp2, XFS_ILOG_CORE);
}
xfs_trans_ichgtime(tp, dp1, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
xfs_trans_log_inode(tp, dp1, XFS_ILOG_CORE);
out:
return error;
}
/* /*
* xfs_rename * xfs_rename
*/ */
...@@ -2665,7 +2785,8 @@ xfs_rename( ...@@ -2665,7 +2785,8 @@ xfs_rename(
xfs_inode_t *src_ip, xfs_inode_t *src_ip,
xfs_inode_t *target_dp, xfs_inode_t *target_dp,
struct xfs_name *target_name, struct xfs_name *target_name,
xfs_inode_t *target_ip) xfs_inode_t *target_ip,
unsigned int flags)
{ {
xfs_trans_t *tp = NULL; xfs_trans_t *tp = NULL;
xfs_mount_t *mp = src_dp->i_mount; xfs_mount_t *mp = src_dp->i_mount;
...@@ -2742,6 +2863,18 @@ xfs_rename( ...@@ -2742,6 +2863,18 @@ xfs_rename(
goto error_return; goto error_return;
} }
/*
* Handle RENAME_EXCHANGE flags
*/
if (flags & RENAME_EXCHANGE) {
error = xfs_cross_rename(tp, src_dp, src_name, src_ip,
target_dp, target_name, target_ip,
&free_list, &first_block, spaceres);
if (error)
goto abort_return;
goto finish_rename;
}
/* /*
* Set up the target. * Set up the target.
*/ */
...@@ -2881,6 +3014,7 @@ xfs_rename( ...@@ -2881,6 +3014,7 @@ xfs_rename(
if (new_parent) if (new_parent)
xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE); xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE);
finish_rename:
/* /*
* If this is a synchronous mount, make sure that the * If this is a synchronous mount, make sure that the
* rename transaction goes to disk before returning to * rename transaction goes to disk before returning to
......
...@@ -338,7 +338,7 @@ int xfs_link(struct xfs_inode *tdp, struct xfs_inode *sip, ...@@ -338,7 +338,7 @@ int xfs_link(struct xfs_inode *tdp, struct xfs_inode *sip,
int xfs_rename(struct xfs_inode *src_dp, struct xfs_name *src_name, int xfs_rename(struct xfs_inode *src_dp, struct xfs_name *src_name,
struct xfs_inode *src_ip, struct xfs_inode *target_dp, struct xfs_inode *src_ip, struct xfs_inode *target_dp,
struct xfs_name *target_name, struct xfs_name *target_name,
struct xfs_inode *target_ip); struct xfs_inode *target_ip, unsigned int flags);
void xfs_ilock(xfs_inode_t *, uint); void xfs_ilock(xfs_inode_t *, uint);
int xfs_ilock_nowait(xfs_inode_t *, uint); int xfs_ilock_nowait(xfs_inode_t *, uint);
...@@ -377,6 +377,15 @@ int xfs_droplink(struct xfs_trans *, struct xfs_inode *); ...@@ -377,6 +377,15 @@ int xfs_droplink(struct xfs_trans *, struct xfs_inode *);
int xfs_bumplink(struct xfs_trans *, struct xfs_inode *); int xfs_bumplink(struct xfs_trans *, struct xfs_inode *);
/* from xfs_file.c */ /* from xfs_file.c */
enum xfs_prealloc_flags {
XFS_PREALLOC_SET = (1 << 1),
XFS_PREALLOC_CLEAR = (1 << 2),
XFS_PREALLOC_SYNC = (1 << 3),
XFS_PREALLOC_INVISIBLE = (1 << 4),
};
int xfs_update_prealloc_flags(struct xfs_inode *,
enum xfs_prealloc_flags);
int xfs_zero_eof(struct xfs_inode *, xfs_off_t, xfs_fsize_t); int xfs_zero_eof(struct xfs_inode *, xfs_off_t, xfs_fsize_t);
int xfs_iozero(struct xfs_inode *, loff_t, size_t); int xfs_iozero(struct xfs_inode *, loff_t, size_t);
......
...@@ -606,11 +606,8 @@ xfs_ioc_space( ...@@ -606,11 +606,8 @@ xfs_ioc_space(
unsigned int cmd, unsigned int cmd,
xfs_flock64_t *bf) xfs_flock64_t *bf)
{ {
struct xfs_mount *mp = ip->i_mount;
struct xfs_trans *tp;
struct iattr iattr; struct iattr iattr;
bool setprealloc = false; enum xfs_prealloc_flags flags = 0;
bool clrprealloc = false;
int error; int error;
/* /*
...@@ -630,6 +627,11 @@ xfs_ioc_space( ...@@ -630,6 +627,11 @@ xfs_ioc_space(
if (!S_ISREG(inode->i_mode)) if (!S_ISREG(inode->i_mode))
return -EINVAL; return -EINVAL;
if (filp->f_flags & O_DSYNC)
flags |= XFS_PREALLOC_SYNC;
if (ioflags & XFS_IO_INVIS)
flags |= XFS_PREALLOC_INVISIBLE;
error = mnt_want_write_file(filp); error = mnt_want_write_file(filp);
if (error) if (error)
return error; return error;
...@@ -673,25 +675,23 @@ xfs_ioc_space( ...@@ -673,25 +675,23 @@ xfs_ioc_space(
} }
if (bf->l_start < 0 || if (bf->l_start < 0 ||
bf->l_start > mp->m_super->s_maxbytes || bf->l_start > inode->i_sb->s_maxbytes ||
bf->l_start + bf->l_len < 0 || bf->l_start + bf->l_len < 0 ||
bf->l_start + bf->l_len >= mp->m_super->s_maxbytes) { bf->l_start + bf->l_len >= inode->i_sb->s_maxbytes) {
error = -EINVAL; error = -EINVAL;
goto out_unlock; goto out_unlock;
} }
switch (cmd) { switch (cmd) {
case XFS_IOC_ZERO_RANGE: case XFS_IOC_ZERO_RANGE:
flags |= XFS_PREALLOC_SET;
error = xfs_zero_file_space(ip, bf->l_start, bf->l_len); error = xfs_zero_file_space(ip, bf->l_start, bf->l_len);
if (!error)
setprealloc = true;
break; break;
case XFS_IOC_RESVSP: case XFS_IOC_RESVSP:
case XFS_IOC_RESVSP64: case XFS_IOC_RESVSP64:
flags |= XFS_PREALLOC_SET;
error = xfs_alloc_file_space(ip, bf->l_start, bf->l_len, error = xfs_alloc_file_space(ip, bf->l_start, bf->l_len,
XFS_BMAPI_PREALLOC); XFS_BMAPI_PREALLOC);
if (!error)
setprealloc = true;
break; break;
case XFS_IOC_UNRESVSP: case XFS_IOC_UNRESVSP:
case XFS_IOC_UNRESVSP64: case XFS_IOC_UNRESVSP64:
...@@ -701,6 +701,7 @@ xfs_ioc_space( ...@@ -701,6 +701,7 @@ xfs_ioc_space(
case XFS_IOC_ALLOCSP64: case XFS_IOC_ALLOCSP64:
case XFS_IOC_FREESP: case XFS_IOC_FREESP:
case XFS_IOC_FREESP64: case XFS_IOC_FREESP64:
flags |= XFS_PREALLOC_CLEAR;
if (bf->l_start > XFS_ISIZE(ip)) { if (bf->l_start > XFS_ISIZE(ip)) {
error = xfs_alloc_file_space(ip, XFS_ISIZE(ip), error = xfs_alloc_file_space(ip, XFS_ISIZE(ip),
bf->l_start - XFS_ISIZE(ip), 0); bf->l_start - XFS_ISIZE(ip), 0);
...@@ -712,8 +713,6 @@ xfs_ioc_space( ...@@ -712,8 +713,6 @@ xfs_ioc_space(
iattr.ia_size = bf->l_start; iattr.ia_size = bf->l_start;
error = xfs_setattr_size(ip, &iattr); error = xfs_setattr_size(ip, &iattr);
if (!error)
clrprealloc = true;
break; break;
default: default:
ASSERT(0); ASSERT(0);
...@@ -723,32 +722,7 @@ xfs_ioc_space( ...@@ -723,32 +722,7 @@ xfs_ioc_space(
if (error) if (error)
goto out_unlock; goto out_unlock;
tp = xfs_trans_alloc(mp, XFS_TRANS_WRITEID); error = xfs_update_prealloc_flags(ip, flags);
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_writeid, 0, 0);
if (error) {
xfs_trans_cancel(tp, 0);
goto out_unlock;
}
xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
if (!(ioflags & XFS_IO_INVIS)) {
ip->i_d.di_mode &= ~S_ISUID;
if (ip->i_d.di_mode & S_IXGRP)
ip->i_d.di_mode &= ~S_ISGID;
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
}
if (setprealloc)
ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC;
else if (clrprealloc)
ip->i_d.di_flags &= ~XFS_DIFLAG_PREALLOC;
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
if (filp->f_flags & O_DSYNC)
xfs_trans_set_sync(tp);
error = xfs_trans_commit(tp, 0);
out_unlock: out_unlock:
xfs_iunlock(ip, XFS_IOLOCK_EXCL); xfs_iunlock(ip, XFS_IOLOCK_EXCL);
...@@ -1013,20 +987,182 @@ xfs_diflags_to_linux( ...@@ -1013,20 +987,182 @@ xfs_diflags_to_linux(
inode->i_flags &= ~S_NOATIME; inode->i_flags &= ~S_NOATIME;
} }
#define FSX_PROJID 1 static int
#define FSX_EXTSIZE 2 xfs_ioctl_setattr_xflags(
#define FSX_XFLAGS 4 struct xfs_trans *tp,
#define FSX_NONBLOCK 8 struct xfs_inode *ip,
struct fsxattr *fa)
{
struct xfs_mount *mp = ip->i_mount;
/* Can't change realtime flag if any extents are allocated. */
if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&
XFS_IS_REALTIME_INODE(ip) != (fa->fsx_xflags & XFS_XFLAG_REALTIME))
return -EINVAL;
/* If realtime flag is set then must have realtime device */
if (fa->fsx_xflags & XFS_XFLAG_REALTIME) {
if (mp->m_sb.sb_rblocks == 0 || mp->m_sb.sb_rextsize == 0 ||
(ip->i_d.di_extsize % mp->m_sb.sb_rextsize))
return -EINVAL;
}
/*
* Can't modify an immutable/append-only file unless
* we have appropriate permission.
*/
if (((ip->i_d.di_flags & (XFS_DIFLAG_IMMUTABLE | XFS_DIFLAG_APPEND)) ||
(fa->fsx_xflags & (XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND))) &&
!capable(CAP_LINUX_IMMUTABLE))
return -EPERM;
xfs_set_diflags(ip, fa->fsx_xflags);
xfs_diflags_to_linux(ip);
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
XFS_STATS_INC(xs_ig_attrchg);
return 0;
}
/*
* Set up the transaction structure for the setattr operation, checking that we
* have permission to do so. On success, return a clean transaction and the
* inode locked exclusively ready for further operation specific checks. On
* failure, return an error without modifying or locking the inode.
*/
static struct xfs_trans *
xfs_ioctl_setattr_get_trans(
struct xfs_inode *ip)
{
struct xfs_mount *mp = ip->i_mount;
struct xfs_trans *tp;
int error;
if (mp->m_flags & XFS_MOUNT_RDONLY)
return ERR_PTR(-EROFS);
if (XFS_FORCED_SHUTDOWN(mp))
return ERR_PTR(-EIO);
tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0);
if (error)
goto out_cancel;
xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
/*
* CAP_FOWNER overrides the following restrictions:
*
* The user ID of the calling process must be equal to the file owner
* ID, except in cases where the CAP_FSETID capability is applicable.
*/
if (!inode_owner_or_capable(VFS_I(ip))) {
error = -EPERM;
goto out_cancel;
}
if (mp->m_flags & XFS_MOUNT_WSYNC)
xfs_trans_set_sync(tp);
return tp;
out_cancel:
xfs_trans_cancel(tp, 0);
return ERR_PTR(error);
}
/*
* extent size hint validation is somewhat cumbersome. Rules are:
*
* 1. extent size hint is only valid for directories and regular files
* 2. XFS_XFLAG_EXTSIZE is only valid for regular files
* 3. XFS_XFLAG_EXTSZINHERIT is only valid for directories.
* 4. can only be changed on regular files if no extents are allocated
* 5. can be changed on directories at any time
* 6. extsize hint of 0 turns off hints, clears inode flags.
* 7. Extent size must be a multiple of the appropriate block size.
* 8. for non-realtime files, the extent size hint must be limited
* to half the AG size to avoid alignment extending the extent beyond the
* limits of the AG.
*/
static int
xfs_ioctl_setattr_check_extsize(
struct xfs_inode *ip,
struct fsxattr *fa)
{
struct xfs_mount *mp = ip->i_mount;
if ((fa->fsx_xflags & XFS_XFLAG_EXTSIZE) && !S_ISREG(ip->i_d.di_mode))
return -EINVAL;
if ((fa->fsx_xflags & XFS_XFLAG_EXTSZINHERIT) &&
!S_ISDIR(ip->i_d.di_mode))
return -EINVAL;
if (S_ISREG(ip->i_d.di_mode) && ip->i_d.di_nextents &&
((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize))
return -EINVAL;
if (fa->fsx_extsize != 0) {
xfs_extlen_t size;
xfs_fsblock_t extsize_fsb;
extsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_extsize);
if (extsize_fsb > MAXEXTLEN)
return -EINVAL;
if (XFS_IS_REALTIME_INODE(ip) ||
(fa->fsx_xflags & XFS_XFLAG_REALTIME)) {
size = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog;
} else {
size = mp->m_sb.sb_blocksize;
if (extsize_fsb > mp->m_sb.sb_agblocks / 2)
return -EINVAL;
}
if (fa->fsx_extsize % size)
return -EINVAL;
} else
fa->fsx_xflags &= ~(XFS_XFLAG_EXTSIZE | XFS_XFLAG_EXTSZINHERIT);
return 0;
}
static int
xfs_ioctl_setattr_check_projid(
struct xfs_inode *ip,
struct fsxattr *fa)
{
/* Disallow 32bit project ids if projid32bit feature is not enabled. */
if (fa->fsx_projid > (__uint16_t)-1 &&
!xfs_sb_version_hasprojid32bit(&ip->i_mount->m_sb))
return -EINVAL;
/*
* Project Quota ID state is only allowed to change from within the init
* namespace. Enforce that restriction only if we are trying to change
* the quota ID state. Everything else is allowed in user namespaces.
*/
if (current_user_ns() == &init_user_ns)
return 0;
if (xfs_get_projid(ip) != fa->fsx_projid)
return -EINVAL;
if ((fa->fsx_xflags & XFS_XFLAG_PROJINHERIT) !=
(ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT))
return -EINVAL;
return 0;
}
STATIC int STATIC int
xfs_ioctl_setattr( xfs_ioctl_setattr(
xfs_inode_t *ip, xfs_inode_t *ip,
struct fsxattr *fa, struct fsxattr *fa)
int mask)
{ {
struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount;
struct xfs_trans *tp; struct xfs_trans *tp;
unsigned int lock_flags = 0;
struct xfs_dquot *udqp = NULL; struct xfs_dquot *udqp = NULL;
struct xfs_dquot *pdqp = NULL; struct xfs_dquot *pdqp = NULL;
struct xfs_dquot *olddquot = NULL; struct xfs_dquot *olddquot = NULL;
...@@ -1034,17 +1170,9 @@ xfs_ioctl_setattr( ...@@ -1034,17 +1170,9 @@ xfs_ioctl_setattr(
trace_xfs_ioctl_setattr(ip); trace_xfs_ioctl_setattr(ip);
if (mp->m_flags & XFS_MOUNT_RDONLY) code = xfs_ioctl_setattr_check_projid(ip, fa);
return -EROFS; if (code)
if (XFS_FORCED_SHUTDOWN(mp)) return code;
return -EIO;
/*
* Disallow 32bit project ids when projid32bit feature is not enabled.
*/
if ((mask & FSX_PROJID) && (fa->fsx_projid > (__uint16_t)-1) &&
!xfs_sb_version_hasprojid32bit(&ip->i_mount->m_sb))
return -EINVAL;
/* /*
* If disk quotas is on, we make sure that the dquots do exist on disk, * If disk quotas is on, we make sure that the dquots do exist on disk,
...@@ -1054,7 +1182,7 @@ xfs_ioctl_setattr( ...@@ -1054,7 +1182,7 @@ xfs_ioctl_setattr(
* If the IDs do change before we take the ilock, we're covered * If the IDs do change before we take the ilock, we're covered
* because the i_*dquot fields will get updated anyway. * because the i_*dquot fields will get updated anyway.
*/ */
if (XFS_IS_QUOTA_ON(mp) && (mask & FSX_PROJID)) { if (XFS_IS_QUOTA_ON(mp)) {
code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid, code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid,
ip->i_d.di_gid, fa->fsx_projid, ip->i_d.di_gid, fa->fsx_projid,
XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp); XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp);
...@@ -1062,175 +1190,49 @@ xfs_ioctl_setattr( ...@@ -1062,175 +1190,49 @@ xfs_ioctl_setattr(
return code; return code;
} }
/* tp = xfs_ioctl_setattr_get_trans(ip);
* For the other attributes, we acquire the inode lock and if (IS_ERR(tp)) {
* first do an error checking pass. code = PTR_ERR(tp);
*/ goto error_free_dquots;
tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);
code = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0);
if (code)
goto error_return;
lock_flags = XFS_ILOCK_EXCL;
xfs_ilock(ip, lock_flags);
/*
* CAP_FOWNER overrides the following restrictions:
*
* The user ID of the calling process must be equal
* to the file owner ID, except in cases where the
* CAP_FSETID capability is applicable.
*/
if (!inode_owner_or_capable(VFS_I(ip))) {
code = -EPERM;
goto error_return;
}
/*
* Do a quota reservation only if projid is actually going to change.
* Only allow changing of projid from init_user_ns since it is a
* non user namespace aware identifier.
*/
if (mask & FSX_PROJID) {
if (current_user_ns() != &init_user_ns) {
code = -EINVAL;
goto error_return;
}
if (XFS_IS_QUOTA_RUNNING(mp) &&
XFS_IS_PQUOTA_ON(mp) &&
xfs_get_projid(ip) != fa->fsx_projid) {
ASSERT(tp);
code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL,
pdqp, capable(CAP_FOWNER) ?
XFS_QMOPT_FORCE_RES : 0);
if (code) /* out of quota */
goto error_return;
}
} }
if (mask & FSX_EXTSIZE) {
/*
* Can't change extent size if any extents are allocated.
*/
if (ip->i_d.di_nextents &&
((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) !=
fa->fsx_extsize)) {
code = -EINVAL; /* EFBIG? */
goto error_return;
}
/* if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) &&
* Extent size must be a multiple of the appropriate block xfs_get_projid(ip) != fa->fsx_projid) {
* size, if set at all. It must also be smaller than the code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL, pdqp,
* maximum extent size supported by the filesystem. capable(CAP_FOWNER) ? XFS_QMOPT_FORCE_RES : 0);
* if (code) /* out of quota */
* Also, for non-realtime files, limit the extent size hint to goto error_trans_cancel;
* half the size of the AGs in the filesystem so alignment
* doesn't result in extents larger than an AG.
*/
if (fa->fsx_extsize != 0) {
xfs_extlen_t size;
xfs_fsblock_t extsize_fsb;
extsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_extsize);
if (extsize_fsb > MAXEXTLEN) {
code = -EINVAL;
goto error_return;
}
if (XFS_IS_REALTIME_INODE(ip) ||
((mask & FSX_XFLAGS) &&
(fa->fsx_xflags & XFS_XFLAG_REALTIME))) {
size = mp->m_sb.sb_rextsize <<
mp->m_sb.sb_blocklog;
} else {
size = mp->m_sb.sb_blocksize;
if (extsize_fsb > mp->m_sb.sb_agblocks / 2) {
code = -EINVAL;
goto error_return;
}
}
if (fa->fsx_extsize % size) {
code = -EINVAL;
goto error_return;
}
}
} }
code = xfs_ioctl_setattr_check_extsize(ip, fa);
if (code)
goto error_trans_cancel;
if (mask & FSX_XFLAGS) { code = xfs_ioctl_setattr_xflags(tp, ip, fa);
/* if (code)
* Can't change realtime flag if any extents are allocated. goto error_trans_cancel;
*/
if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&
(XFS_IS_REALTIME_INODE(ip)) !=
(fa->fsx_xflags & XFS_XFLAG_REALTIME)) {
code = -EINVAL; /* EFBIG? */
goto error_return;
}
/*
* If realtime flag is set then must have realtime data.
*/
if ((fa->fsx_xflags & XFS_XFLAG_REALTIME)) {
if ((mp->m_sb.sb_rblocks == 0) ||
(mp->m_sb.sb_rextsize == 0) ||
(ip->i_d.di_extsize % mp->m_sb.sb_rextsize)) {
code = -EINVAL;
goto error_return;
}
}
/*
* Can't modify an immutable/append-only file unless
* we have appropriate permission.
*/
if ((ip->i_d.di_flags &
(XFS_DIFLAG_IMMUTABLE|XFS_DIFLAG_APPEND) ||
(fa->fsx_xflags &
(XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND))) &&
!capable(CAP_LINUX_IMMUTABLE)) {
code = -EPERM;
goto error_return;
}
}
xfs_trans_ijoin(tp, ip, 0);
/* /*
* Change file ownership. Must be the owner or privileged. * Change file ownership. Must be the owner or privileged. CAP_FSETID
* overrides the following restrictions:
*
* The set-user-ID and set-group-ID bits of a file will be cleared upon
* successful return from chown()
*/ */
if (mask & FSX_PROJID) {
/*
* CAP_FSETID overrides the following restrictions:
*
* The set-user-ID and set-group-ID bits of a file will be
* cleared upon successful return from chown()
*/
if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) &&
!capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID))
ip->i_d.di_mode &= ~(S_ISUID|S_ISGID);
/*
* Change the ownerships and register quota modifications
* in the transaction.
*/
if (xfs_get_projid(ip) != fa->fsx_projid) {
if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) {
olddquot = xfs_qm_vop_chown(tp, ip,
&ip->i_pdquot, pdqp);
}
ASSERT(ip->i_d.di_version > 1);
xfs_set_projid(ip, fa->fsx_projid);
}
} if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) &&
!capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID))
ip->i_d.di_mode &= ~(S_ISUID|S_ISGID);
if (mask & FSX_XFLAGS) { /* Change the ownerships and register project quota modifications */
xfs_set_diflags(ip, fa->fsx_xflags); if (xfs_get_projid(ip) != fa->fsx_projid) {
xfs_diflags_to_linux(ip); if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) {
olddquot = xfs_qm_vop_chown(tp, ip,
&ip->i_pdquot, pdqp);
}
ASSERT(ip->i_d.di_version > 1);
xfs_set_projid(ip, fa->fsx_projid);
} }
/* /*
...@@ -1238,34 +1240,12 @@ xfs_ioctl_setattr( ...@@ -1238,34 +1240,12 @@ xfs_ioctl_setattr(
* extent size hint should be set on the inode. If no extent size flags * extent size hint should be set on the inode. If no extent size flags
* are set on the inode then unconditionally clear the extent size hint. * are set on the inode then unconditionally clear the extent size hint.
*/ */
if (mask & FSX_EXTSIZE) { if (ip->i_d.di_flags & (XFS_DIFLAG_EXTSIZE | XFS_DIFLAG_EXTSZINHERIT))
int extsize = 0; ip->i_d.di_extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog;
else
if (ip->i_d.di_flags & ip->i_d.di_extsize = 0;
(XFS_DIFLAG_EXTSIZE | XFS_DIFLAG_EXTSZINHERIT))
extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog;
ip->i_d.di_extsize = extsize;
}
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
XFS_STATS_INC(xs_ig_attrchg);
/*
* If this is a synchronous mount, make sure that the
* transaction goes to disk before returning to the user.
* This is slightly sub-optimal in that truncates require
* two sync transactions instead of one for wsync filesystems.
* One for the truncate and one for the timestamps since we
* don't want to change the timestamps unless we're sure the
* truncate worked. Truncates are less than 1% of the laddis
* mix so this probably isn't worth the trouble to optimize.
*/
if (mp->m_flags & XFS_MOUNT_WSYNC)
xfs_trans_set_sync(tp);
code = xfs_trans_commit(tp, 0); code = xfs_trans_commit(tp, 0);
xfs_iunlock(ip, lock_flags);
/* /*
* Release any dquot(s) the inode had kept before chown. * Release any dquot(s) the inode had kept before chown.
...@@ -1276,12 +1256,11 @@ xfs_ioctl_setattr( ...@@ -1276,12 +1256,11 @@ xfs_ioctl_setattr(
return code; return code;
error_return: error_trans_cancel:
xfs_trans_cancel(tp, 0);
error_free_dquots:
xfs_qm_dqrele(udqp); xfs_qm_dqrele(udqp);
xfs_qm_dqrele(pdqp); xfs_qm_dqrele(pdqp);
xfs_trans_cancel(tp, 0);
if (lock_flags)
xfs_iunlock(ip, lock_flags);
return code; return code;
} }
...@@ -1292,20 +1271,15 @@ xfs_ioc_fssetxattr( ...@@ -1292,20 +1271,15 @@ xfs_ioc_fssetxattr(
void __user *arg) void __user *arg)
{ {
struct fsxattr fa; struct fsxattr fa;
unsigned int mask;
int error; int error;
if (copy_from_user(&fa, arg, sizeof(fa))) if (copy_from_user(&fa, arg, sizeof(fa)))
return -EFAULT; return -EFAULT;
mask = FSX_XFLAGS | FSX_EXTSIZE | FSX_PROJID;
if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
mask |= FSX_NONBLOCK;
error = mnt_want_write_file(filp); error = mnt_want_write_file(filp);
if (error) if (error)
return error; return error;
error = xfs_ioctl_setattr(ip, &fa, mask); error = xfs_ioctl_setattr(ip, &fa);
mnt_drop_write_file(filp); mnt_drop_write_file(filp);
return error; return error;
} }
...@@ -1325,14 +1299,14 @@ xfs_ioc_getxflags( ...@@ -1325,14 +1299,14 @@ xfs_ioc_getxflags(
STATIC int STATIC int
xfs_ioc_setxflags( xfs_ioc_setxflags(
xfs_inode_t *ip, struct xfs_inode *ip,
struct file *filp, struct file *filp,
void __user *arg) void __user *arg)
{ {
struct xfs_trans *tp;
struct fsxattr fa; struct fsxattr fa;
unsigned int flags; unsigned int flags;
unsigned int mask; int error;
int error;
if (copy_from_user(&flags, arg, sizeof(flags))) if (copy_from_user(&flags, arg, sizeof(flags)))
return -EFAULT; return -EFAULT;
...@@ -1342,15 +1316,26 @@ xfs_ioc_setxflags( ...@@ -1342,15 +1316,26 @@ xfs_ioc_setxflags(
FS_SYNC_FL)) FS_SYNC_FL))
return -EOPNOTSUPP; return -EOPNOTSUPP;
mask = FSX_XFLAGS;
if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
mask |= FSX_NONBLOCK;
fa.fsx_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip)); fa.fsx_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip));
error = mnt_want_write_file(filp); error = mnt_want_write_file(filp);
if (error) if (error)
return error; return error;
error = xfs_ioctl_setattr(ip, &fa, mask);
tp = xfs_ioctl_setattr_get_trans(ip);
if (IS_ERR(tp)) {
error = PTR_ERR(tp);
goto out_drop_write;
}
error = xfs_ioctl_setattr_xflags(tp, ip, &fa);
if (error) {
xfs_trans_cancel(tp, 0);
goto out_drop_write;
}
error = xfs_trans_commit(tp, 0);
out_drop_write:
mnt_drop_write_file(filp); mnt_drop_write_file(filp);
return error; return error;
} }
......
...@@ -423,7 +423,7 @@ xfs_compat_attrmulti_by_handle( ...@@ -423,7 +423,7 @@ xfs_compat_attrmulti_by_handle(
ops = memdup_user(compat_ptr(am_hreq.ops), size); ops = memdup_user(compat_ptr(am_hreq.ops), size);
if (IS_ERR(ops)) { if (IS_ERR(ops)) {
error = -PTR_ERR(ops); error = PTR_ERR(ops);
goto out_dput; goto out_dput;
} }
......
...@@ -802,7 +802,7 @@ int ...@@ -802,7 +802,7 @@ int
xfs_iomap_write_unwritten( xfs_iomap_write_unwritten(
xfs_inode_t *ip, xfs_inode_t *ip,
xfs_off_t offset, xfs_off_t offset,
size_t count) xfs_off_t count)
{ {
xfs_mount_t *mp = ip->i_mount; xfs_mount_t *mp = ip->i_mount;
xfs_fileoff_t offset_fsb; xfs_fileoff_t offset_fsb;
......
...@@ -27,6 +27,6 @@ int xfs_iomap_write_delay(struct xfs_inode *, xfs_off_t, size_t, ...@@ -27,6 +27,6 @@ int xfs_iomap_write_delay(struct xfs_inode *, xfs_off_t, size_t,
struct xfs_bmbt_irec *); struct xfs_bmbt_irec *);
int xfs_iomap_write_allocate(struct xfs_inode *, xfs_off_t, int xfs_iomap_write_allocate(struct xfs_inode *, xfs_off_t,
struct xfs_bmbt_irec *); struct xfs_bmbt_irec *);
int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, size_t); int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, xfs_off_t);
#endif /* __XFS_IOMAP_H__*/ #endif /* __XFS_IOMAP_H__*/
...@@ -380,18 +380,27 @@ xfs_vn_rename( ...@@ -380,18 +380,27 @@ xfs_vn_rename(
struct inode *odir, struct inode *odir,
struct dentry *odentry, struct dentry *odentry,
struct inode *ndir, struct inode *ndir,
struct dentry *ndentry) struct dentry *ndentry,
unsigned int flags)
{ {
struct inode *new_inode = ndentry->d_inode; struct inode *new_inode = ndentry->d_inode;
int omode = 0;
struct xfs_name oname; struct xfs_name oname;
struct xfs_name nname; struct xfs_name nname;
xfs_dentry_to_name(&oname, odentry, 0); if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
return -EINVAL;
/* if we are exchanging files, we need to set i_mode of both files */
if (flags & RENAME_EXCHANGE)
omode = ndentry->d_inode->i_mode;
xfs_dentry_to_name(&oname, odentry, omode);
xfs_dentry_to_name(&nname, ndentry, odentry->d_inode->i_mode); xfs_dentry_to_name(&nname, ndentry, odentry->d_inode->i_mode);
return xfs_rename(XFS_I(odir), &oname, XFS_I(odentry->d_inode), return xfs_rename(XFS_I(odir), &oname, XFS_I(odentry->d_inode),
XFS_I(ndir), &nname, new_inode ? XFS_I(ndir), &nname,
XFS_I(new_inode) : NULL); new_inode ? XFS_I(new_inode) : NULL, flags);
} }
/* /*
...@@ -1144,7 +1153,7 @@ static const struct inode_operations xfs_dir_inode_operations = { ...@@ -1144,7 +1153,7 @@ static const struct inode_operations xfs_dir_inode_operations = {
*/ */
.rmdir = xfs_vn_unlink, .rmdir = xfs_vn_unlink,
.mknod = xfs_vn_mknod, .mknod = xfs_vn_mknod,
.rename = xfs_vn_rename, .rename2 = xfs_vn_rename,
.get_acl = xfs_get_acl, .get_acl = xfs_get_acl,
.set_acl = xfs_set_acl, .set_acl = xfs_set_acl,
.getattr = xfs_vn_getattr, .getattr = xfs_vn_getattr,
...@@ -1172,7 +1181,7 @@ static const struct inode_operations xfs_dir_ci_inode_operations = { ...@@ -1172,7 +1181,7 @@ static const struct inode_operations xfs_dir_ci_inode_operations = {
*/ */
.rmdir = xfs_vn_unlink, .rmdir = xfs_vn_unlink,
.mknod = xfs_vn_mknod, .mknod = xfs_vn_mknod,
.rename = xfs_vn_rename, .rename2 = xfs_vn_rename,
.get_acl = xfs_get_acl, .get_acl = xfs_get_acl,
.set_acl = xfs_set_acl, .set_acl = xfs_set_acl,
.getattr = xfs_vn_getattr, .getattr = xfs_vn_getattr,
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "xfs_fsops.h" #include "xfs_fsops.h"
#include "xfs_cksum.h" #include "xfs_cksum.h"
#include "xfs_sysfs.h" #include "xfs_sysfs.h"
#include "xfs_sb.h"
kmem_zone_t *xfs_log_ticket_zone; kmem_zone_t *xfs_log_ticket_zone;
...@@ -1290,9 +1291,20 @@ xfs_log_worker( ...@@ -1290,9 +1291,20 @@ xfs_log_worker(
struct xfs_mount *mp = log->l_mp; struct xfs_mount *mp = log->l_mp;
/* dgc: errors ignored - not fatal and nowhere to report them */ /* dgc: errors ignored - not fatal and nowhere to report them */
if (xfs_log_need_covered(mp)) if (xfs_log_need_covered(mp)) {
xfs_fs_log_dummy(mp); /*
else * Dump a transaction into the log that contains no real change.
* This is needed to stamp the current tail LSN into the log
* during the covering operation.
*
* We cannot use an inode here for this - that will push dirty
* state back up into the VFS and then periodic inode flushing
* will prevent log covering from making progress. Hence we
* synchronously log the superblock instead to ensure the
* superblock is immediately unpinned and can be written back.
*/
xfs_sync_sb(mp, true);
} else
xfs_log_force(mp, 0); xfs_log_force(mp, 0);
/* start pushing all the metadata that is currently dirty */ /* start pushing all the metadata that is currently dirty */
...@@ -1395,6 +1407,8 @@ xlog_alloc_log( ...@@ -1395,6 +1407,8 @@ xlog_alloc_log(
ASSERT(xfs_buf_islocked(bp)); ASSERT(xfs_buf_islocked(bp));
xfs_buf_unlock(bp); xfs_buf_unlock(bp);
/* use high priority wq for log I/O completion */
bp->b_ioend_wq = mp->m_log_workqueue;
bp->b_iodone = xlog_iodone; bp->b_iodone = xlog_iodone;
log->l_xbuf = bp; log->l_xbuf = bp;
...@@ -1427,6 +1441,8 @@ xlog_alloc_log( ...@@ -1427,6 +1441,8 @@ xlog_alloc_log(
ASSERT(xfs_buf_islocked(bp)); ASSERT(xfs_buf_islocked(bp));
xfs_buf_unlock(bp); xfs_buf_unlock(bp);
/* use high priority wq for log I/O completion */
bp->b_ioend_wq = mp->m_log_workqueue;
bp->b_iodone = xlog_iodone; bp->b_iodone = xlog_iodone;
iclog->ic_bp = bp; iclog->ic_bp = bp;
iclog->ic_data = bp->b_addr; iclog->ic_data = bp->b_addr;
...@@ -1806,8 +1822,6 @@ xlog_sync( ...@@ -1806,8 +1822,6 @@ xlog_sync(
XFS_BUF_ZEROFLAGS(bp); XFS_BUF_ZEROFLAGS(bp);
XFS_BUF_ASYNC(bp); XFS_BUF_ASYNC(bp);
bp->b_flags |= XBF_SYNCIO; bp->b_flags |= XBF_SYNCIO;
/* use high priority completion wq */
bp->b_ioend_wq = log->l_mp->m_log_workqueue;
if (log->l_mp->m_flags & XFS_MOUNT_BARRIER) { if (log->l_mp->m_flags & XFS_MOUNT_BARRIER) {
bp->b_flags |= XBF_FUA; bp->b_flags |= XBF_FUA;
...@@ -1856,8 +1870,6 @@ xlog_sync( ...@@ -1856,8 +1870,6 @@ xlog_sync(
bp->b_flags |= XBF_SYNCIO; bp->b_flags |= XBF_SYNCIO;
if (log->l_mp->m_flags & XFS_MOUNT_BARRIER) if (log->l_mp->m_flags & XFS_MOUNT_BARRIER)
bp->b_flags |= XBF_FUA; bp->b_flags |= XBF_FUA;
/* use high priority completion wq */
bp->b_ioend_wq = log->l_mp->m_log_workqueue;
ASSERT(XFS_BUF_ADDR(bp) <= log->l_logBBsize-1); ASSERT(XFS_BUF_ADDR(bp) <= log->l_logBBsize-1);
ASSERT(XFS_BUF_ADDR(bp) + BTOBB(count) <= log->l_logBBsize); ASSERT(XFS_BUF_ADDR(bp) + BTOBB(count) <= log->l_logBBsize);
...@@ -2027,7 +2039,7 @@ xlog_print_tic_res( ...@@ -2027,7 +2039,7 @@ xlog_print_tic_res(
" total reg = %u bytes (o/flow = %u bytes)\n" " total reg = %u bytes (o/flow = %u bytes)\n"
" ophdrs = %u (ophdr space = %u bytes)\n" " ophdrs = %u (ophdr space = %u bytes)\n"
" ophdr + reg = %u bytes\n" " ophdr + reg = %u bytes\n"
" num regions = %u\n", " num regions = %u",
((ticket->t_trans_type <= 0 || ((ticket->t_trans_type <= 0 ||
ticket->t_trans_type > XFS_TRANS_TYPE_MAX) ? ticket->t_trans_type > XFS_TRANS_TYPE_MAX) ?
"bad-trans-type" : trans_type_str[ticket->t_trans_type-1]), "bad-trans-type" : trans_type_str[ticket->t_trans_type-1]),
......
...@@ -408,11 +408,11 @@ xfs_update_alignment(xfs_mount_t *mp) ...@@ -408,11 +408,11 @@ xfs_update_alignment(xfs_mount_t *mp)
if (xfs_sb_version_hasdalign(sbp)) { if (xfs_sb_version_hasdalign(sbp)) {
if (sbp->sb_unit != mp->m_dalign) { if (sbp->sb_unit != mp->m_dalign) {
sbp->sb_unit = mp->m_dalign; sbp->sb_unit = mp->m_dalign;
mp->m_update_flags |= XFS_SB_UNIT; mp->m_update_sb = true;
} }
if (sbp->sb_width != mp->m_swidth) { if (sbp->sb_width != mp->m_swidth) {
sbp->sb_width = mp->m_swidth; sbp->sb_width = mp->m_swidth;
mp->m_update_flags |= XFS_SB_WIDTH; mp->m_update_sb = true;
} }
} else { } else {
xfs_warn(mp, xfs_warn(mp,
...@@ -583,38 +583,19 @@ int ...@@ -583,38 +583,19 @@ int
xfs_mount_reset_sbqflags( xfs_mount_reset_sbqflags(
struct xfs_mount *mp) struct xfs_mount *mp)
{ {
int error;
struct xfs_trans *tp;
mp->m_qflags = 0; mp->m_qflags = 0;
/* /* It is OK to look at sb_qflags in the mount path without m_sb_lock. */
* It is OK to look at sb_qflags here in mount path,
* without m_sb_lock.
*/
if (mp->m_sb.sb_qflags == 0) if (mp->m_sb.sb_qflags == 0)
return 0; return 0;
spin_lock(&mp->m_sb_lock); spin_lock(&mp->m_sb_lock);
mp->m_sb.sb_qflags = 0; mp->m_sb.sb_qflags = 0;
spin_unlock(&mp->m_sb_lock); spin_unlock(&mp->m_sb_lock);
/* if (!xfs_fs_writable(mp, SB_FREEZE_WRITE))
* If the fs is readonly, let the incore superblock run
* with quotas off but don't flush the update out to disk
*/
if (mp->m_flags & XFS_MOUNT_RDONLY)
return 0; return 0;
tp = xfs_trans_alloc(mp, XFS_TRANS_QM_SBCHANGE); return xfs_sync_sb(mp, false);
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_qm_sbchange, 0, 0);
if (error) {
xfs_trans_cancel(tp, 0);
xfs_alert(mp, "%s: Superblock update failed!", __func__);
return error;
}
xfs_mod_sb(tp, XFS_SB_QFLAGS);
return xfs_trans_commit(tp, 0);
} }
__uint64_t __uint64_t
...@@ -659,26 +640,25 @@ xfs_mountfs( ...@@ -659,26 +640,25 @@ xfs_mountfs(
xfs_sb_mount_common(mp, sbp); xfs_sb_mount_common(mp, sbp);
/* /*
* Check for a mismatched features2 values. Older kernels * Check for a mismatched features2 values. Older kernels read & wrote
* read & wrote into the wrong sb offset for sb_features2 * into the wrong sb offset for sb_features2 on some platforms due to
* on some platforms due to xfs_sb_t not being 64bit size aligned * xfs_sb_t not being 64bit size aligned when sb_features2 was added,
* when sb_features2 was added, which made older superblock * which made older superblock reading/writing routines swap it as a
* reading/writing routines swap it as a 64-bit value. * 64-bit value.
* *
* For backwards compatibility, we make both slots equal. * For backwards compatibility, we make both slots equal.
* *
* If we detect a mismatched field, we OR the set bits into the * If we detect a mismatched field, we OR the set bits into the existing
* existing features2 field in case it has already been modified; we * features2 field in case it has already been modified; we don't want
* don't want to lose any features. We then update the bad location * to lose any features. We then update the bad location with the ORed
* with the ORed value so that older kernels will see any features2 * value so that older kernels will see any features2 flags. The
* flags, and mark the two fields as needing updates once the * superblock writeback code ensures the new sb_features2 is copied to
* transaction subsystem is online. * sb_bad_features2 before it is logged or written to disk.
*/ */
if (xfs_sb_has_mismatched_features2(sbp)) { if (xfs_sb_has_mismatched_features2(sbp)) {
xfs_warn(mp, "correcting sb_features alignment problem"); xfs_warn(mp, "correcting sb_features alignment problem");
sbp->sb_features2 |= sbp->sb_bad_features2; sbp->sb_features2 |= sbp->sb_bad_features2;
sbp->sb_bad_features2 = sbp->sb_features2; mp->m_update_sb = true;
mp->m_update_flags |= XFS_SB_FEATURES2 | XFS_SB_BAD_FEATURES2;
/* /*
* Re-check for ATTR2 in case it was found in bad_features2 * Re-check for ATTR2 in case it was found in bad_features2
...@@ -692,17 +672,17 @@ xfs_mountfs( ...@@ -692,17 +672,17 @@ xfs_mountfs(
if (xfs_sb_version_hasattr2(&mp->m_sb) && if (xfs_sb_version_hasattr2(&mp->m_sb) &&
(mp->m_flags & XFS_MOUNT_NOATTR2)) { (mp->m_flags & XFS_MOUNT_NOATTR2)) {
xfs_sb_version_removeattr2(&mp->m_sb); xfs_sb_version_removeattr2(&mp->m_sb);
mp->m_update_flags |= XFS_SB_FEATURES2; mp->m_update_sb = true;
/* update sb_versionnum for the clearing of the morebits */ /* update sb_versionnum for the clearing of the morebits */
if (!sbp->sb_features2) if (!sbp->sb_features2)
mp->m_update_flags |= XFS_SB_VERSIONNUM; mp->m_update_sb = true;
} }
/* always use v2 inodes by default now */ /* always use v2 inodes by default now */
if (!(mp->m_sb.sb_versionnum & XFS_SB_VERSION_NLINKBIT)) { if (!(mp->m_sb.sb_versionnum & XFS_SB_VERSION_NLINKBIT)) {
mp->m_sb.sb_versionnum |= XFS_SB_VERSION_NLINKBIT; mp->m_sb.sb_versionnum |= XFS_SB_VERSION_NLINKBIT;
mp->m_update_flags |= XFS_SB_VERSIONNUM; mp->m_update_sb = true;
} }
/* /*
...@@ -895,8 +875,8 @@ xfs_mountfs( ...@@ -895,8 +875,8 @@ xfs_mountfs(
* the next remount into writeable mode. Otherwise we would never * the next remount into writeable mode. Otherwise we would never
* perform the update e.g. for the root filesystem. * perform the update e.g. for the root filesystem.
*/ */
if (mp->m_update_flags && !(mp->m_flags & XFS_MOUNT_RDONLY)) { if (mp->m_update_sb && !(mp->m_flags & XFS_MOUNT_RDONLY)) {
error = xfs_mount_log_sb(mp, mp->m_update_flags); error = xfs_sync_sb(mp, false);
if (error) { if (error) {
xfs_warn(mp, "failed to write sb changes"); xfs_warn(mp, "failed to write sb changes");
goto out_rtunmount; goto out_rtunmount;
...@@ -1103,9 +1083,6 @@ xfs_fs_writable( ...@@ -1103,9 +1083,6 @@ xfs_fs_writable(
int int
xfs_log_sbcount(xfs_mount_t *mp) xfs_log_sbcount(xfs_mount_t *mp)
{ {
xfs_trans_t *tp;
int error;
/* allow this to proceed during the freeze sequence... */ /* allow this to proceed during the freeze sequence... */
if (!xfs_fs_writable(mp, SB_FREEZE_COMPLETE)) if (!xfs_fs_writable(mp, SB_FREEZE_COMPLETE))
return 0; return 0;
...@@ -1119,17 +1096,7 @@ xfs_log_sbcount(xfs_mount_t *mp) ...@@ -1119,17 +1096,7 @@ xfs_log_sbcount(xfs_mount_t *mp)
if (!xfs_sb_version_haslazysbcount(&mp->m_sb)) if (!xfs_sb_version_haslazysbcount(&mp->m_sb))
return 0; return 0;
tp = _xfs_trans_alloc(mp, XFS_TRANS_SB_COUNT, KM_SLEEP); return xfs_sync_sb(mp, true);
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_sb, 0, 0);
if (error) {
xfs_trans_cancel(tp, 0);
return error;
}
xfs_mod_sb(tp, XFS_SB_IFREE | XFS_SB_ICOUNT | XFS_SB_FDBLOCKS);
xfs_trans_set_sync(tp);
error = xfs_trans_commit(tp, 0);
return error;
} }
/* /*
...@@ -1422,34 +1389,6 @@ xfs_freesb( ...@@ -1422,34 +1389,6 @@ xfs_freesb(
xfs_buf_relse(bp); xfs_buf_relse(bp);
} }
/*
* Used to log changes to the superblock unit and width fields which could
* be altered by the mount options, as well as any potential sb_features2
* fixup. Only the first superblock is updated.
*/
int
xfs_mount_log_sb(
xfs_mount_t *mp,
__int64_t fields)
{
xfs_trans_t *tp;
int error;
ASSERT(fields & (XFS_SB_UNIT | XFS_SB_WIDTH | XFS_SB_UUID |
XFS_SB_FEATURES2 | XFS_SB_BAD_FEATURES2 |
XFS_SB_VERSIONNUM));
tp = xfs_trans_alloc(mp, XFS_TRANS_SB_UNIT);
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_sb, 0, 0);
if (error) {
xfs_trans_cancel(tp, 0);
return error;
}
xfs_mod_sb(tp, fields);
error = xfs_trans_commit(tp, 0);
return error;
}
/* /*
* If the underlying (data/log/rt) device is readonly, there are some * If the underlying (data/log/rt) device is readonly, there are some
* operations that cannot proceed. * operations that cannot proceed.
......
...@@ -162,8 +162,7 @@ typedef struct xfs_mount { ...@@ -162,8 +162,7 @@ typedef struct xfs_mount {
struct delayed_work m_reclaim_work; /* background inode reclaim */ struct delayed_work m_reclaim_work; /* background inode reclaim */
struct delayed_work m_eofblocks_work; /* background eof blocks struct delayed_work m_eofblocks_work; /* background eof blocks
trimming */ trimming */
__int64_t m_update_flags; /* sb flags we need to update bool m_update_sb; /* sb needs update in mount */
on the next remount,rw */
int64_t m_low_space[XFS_LOWSP_MAX]; int64_t m_low_space[XFS_LOWSP_MAX];
/* low free space thresholds */ /* low free space thresholds */
struct xfs_kobj m_kobj; struct xfs_kobj m_kobj;
...@@ -378,7 +377,7 @@ extern void xfs_unmountfs(xfs_mount_t *); ...@@ -378,7 +377,7 @@ extern void xfs_unmountfs(xfs_mount_t *);
extern int xfs_mod_incore_sb(xfs_mount_t *, xfs_sb_field_t, int64_t, int); extern int xfs_mod_incore_sb(xfs_mount_t *, xfs_sb_field_t, int64_t, int);
extern int xfs_mod_incore_sb_batch(xfs_mount_t *, xfs_mod_sb_t *, extern int xfs_mod_incore_sb_batch(xfs_mount_t *, xfs_mod_sb_t *,
uint, int); uint, int);
extern int xfs_mount_log_sb(xfs_mount_t *, __int64_t); extern int xfs_mount_log_sb(xfs_mount_t *);
extern struct xfs_buf *xfs_getsb(xfs_mount_t *, int); extern struct xfs_buf *xfs_getsb(xfs_mount_t *, int);
extern int xfs_readsb(xfs_mount_t *, int); extern int xfs_readsb(xfs_mount_t *, int);
extern void xfs_freesb(xfs_mount_t *); extern void xfs_freesb(xfs_mount_t *);
......
...@@ -714,7 +714,6 @@ STATIC int ...@@ -714,7 +714,6 @@ STATIC int
xfs_qm_qino_alloc( xfs_qm_qino_alloc(
xfs_mount_t *mp, xfs_mount_t *mp,
xfs_inode_t **ip, xfs_inode_t **ip,
__int64_t sbfields,
uint flags) uint flags)
{ {
xfs_trans_t *tp; xfs_trans_t *tp;
...@@ -777,11 +776,6 @@ xfs_qm_qino_alloc( ...@@ -777,11 +776,6 @@ xfs_qm_qino_alloc(
spin_lock(&mp->m_sb_lock); spin_lock(&mp->m_sb_lock);
if (flags & XFS_QMOPT_SBVERSION) { if (flags & XFS_QMOPT_SBVERSION) {
ASSERT(!xfs_sb_version_hasquota(&mp->m_sb)); ASSERT(!xfs_sb_version_hasquota(&mp->m_sb));
ASSERT((sbfields & (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO |
XFS_SB_GQUOTINO | XFS_SB_PQUOTINO | XFS_SB_QFLAGS)) ==
(XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO |
XFS_SB_GQUOTINO | XFS_SB_PQUOTINO |
XFS_SB_QFLAGS));
xfs_sb_version_addquota(&mp->m_sb); xfs_sb_version_addquota(&mp->m_sb);
mp->m_sb.sb_uquotino = NULLFSINO; mp->m_sb.sb_uquotino = NULLFSINO;
...@@ -798,7 +792,7 @@ xfs_qm_qino_alloc( ...@@ -798,7 +792,7 @@ xfs_qm_qino_alloc(
else else
mp->m_sb.sb_pquotino = (*ip)->i_ino; mp->m_sb.sb_pquotino = (*ip)->i_ino;
spin_unlock(&mp->m_sb_lock); spin_unlock(&mp->m_sb_lock);
xfs_mod_sb(tp, sbfields); xfs_log_sb(tp);
if ((error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES))) { if ((error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES))) {
xfs_alert(mp, "%s failed (error %d)!", __func__, error); xfs_alert(mp, "%s failed (error %d)!", __func__, error);
...@@ -1451,7 +1445,7 @@ xfs_qm_mount_quotas( ...@@ -1451,7 +1445,7 @@ xfs_qm_mount_quotas(
spin_unlock(&mp->m_sb_lock); spin_unlock(&mp->m_sb_lock);
if (sbf != (mp->m_qflags & XFS_MOUNT_QUOTA_ALL)) { if (sbf != (mp->m_qflags & XFS_MOUNT_QUOTA_ALL)) {
if (xfs_qm_write_sb_changes(mp, XFS_SB_QFLAGS)) { if (xfs_sync_sb(mp, false)) {
/* /*
* We could only have been turning quotas off. * We could only have been turning quotas off.
* We aren't in very good shape actually because * We aren't in very good shape actually because
...@@ -1482,7 +1476,6 @@ xfs_qm_init_quotainos( ...@@ -1482,7 +1476,6 @@ xfs_qm_init_quotainos(
struct xfs_inode *gip = NULL; struct xfs_inode *gip = NULL;
struct xfs_inode *pip = NULL; struct xfs_inode *pip = NULL;
int error; int error;
__int64_t sbflags = 0;
uint flags = 0; uint flags = 0;
ASSERT(mp->m_quotainfo); ASSERT(mp->m_quotainfo);
...@@ -1517,9 +1510,6 @@ xfs_qm_init_quotainos( ...@@ -1517,9 +1510,6 @@ xfs_qm_init_quotainos(
} }
} else { } else {
flags |= XFS_QMOPT_SBVERSION; flags |= XFS_QMOPT_SBVERSION;
sbflags |= (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO |
XFS_SB_GQUOTINO | XFS_SB_PQUOTINO |
XFS_SB_QFLAGS);
} }
/* /*
...@@ -1530,7 +1520,6 @@ xfs_qm_init_quotainos( ...@@ -1530,7 +1520,6 @@ xfs_qm_init_quotainos(
*/ */
if (XFS_IS_UQUOTA_ON(mp) && uip == NULL) { if (XFS_IS_UQUOTA_ON(mp) && uip == NULL) {
error = xfs_qm_qino_alloc(mp, &uip, error = xfs_qm_qino_alloc(mp, &uip,
sbflags | XFS_SB_UQUOTINO,
flags | XFS_QMOPT_UQUOTA); flags | XFS_QMOPT_UQUOTA);
if (error) if (error)
goto error_rele; goto error_rele;
...@@ -1539,7 +1528,6 @@ xfs_qm_init_quotainos( ...@@ -1539,7 +1528,6 @@ xfs_qm_init_quotainos(
} }
if (XFS_IS_GQUOTA_ON(mp) && gip == NULL) { if (XFS_IS_GQUOTA_ON(mp) && gip == NULL) {
error = xfs_qm_qino_alloc(mp, &gip, error = xfs_qm_qino_alloc(mp, &gip,
sbflags | XFS_SB_GQUOTINO,
flags | XFS_QMOPT_GQUOTA); flags | XFS_QMOPT_GQUOTA);
if (error) if (error)
goto error_rele; goto error_rele;
...@@ -1548,7 +1536,6 @@ xfs_qm_init_quotainos( ...@@ -1548,7 +1536,6 @@ xfs_qm_init_quotainos(
} }
if (XFS_IS_PQUOTA_ON(mp) && pip == NULL) { if (XFS_IS_PQUOTA_ON(mp) && pip == NULL) {
error = xfs_qm_qino_alloc(mp, &pip, error = xfs_qm_qino_alloc(mp, &pip,
sbflags | XFS_SB_PQUOTINO,
flags | XFS_QMOPT_PQUOTA); flags | XFS_QMOPT_PQUOTA);
if (error) if (error)
goto error_rele; goto error_rele;
...@@ -1587,32 +1574,6 @@ xfs_qm_dqfree_one( ...@@ -1587,32 +1574,6 @@ xfs_qm_dqfree_one(
xfs_qm_dqdestroy(dqp); xfs_qm_dqdestroy(dqp);
} }
/*
* Start a transaction and write the incore superblock changes to
* disk. flags parameter indicates which fields have changed.
*/
int
xfs_qm_write_sb_changes(
xfs_mount_t *mp,
__int64_t flags)
{
xfs_trans_t *tp;
int error;
tp = xfs_trans_alloc(mp, XFS_TRANS_QM_SBCHANGE);
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_qm_sbchange, 0, 0);
if (error) {
xfs_trans_cancel(tp, 0);
return error;
}
xfs_mod_sb(tp, flags);
error = xfs_trans_commit(tp, 0);
return error;
}
/* --------------- utility functions for vnodeops ---------------- */ /* --------------- utility functions for vnodeops ---------------- */
......
...@@ -157,7 +157,6 @@ struct xfs_dquot_acct { ...@@ -157,7 +157,6 @@ struct xfs_dquot_acct {
#define XFS_QM_RTBWARNLIMIT 5 #define XFS_QM_RTBWARNLIMIT 5
extern void xfs_qm_destroy_quotainfo(struct xfs_mount *); extern void xfs_qm_destroy_quotainfo(struct xfs_mount *);
extern int xfs_qm_write_sb_changes(struct xfs_mount *, __int64_t);
/* dquot stuff */ /* dquot stuff */
extern void xfs_qm_dqpurge_all(struct xfs_mount *, uint); extern void xfs_qm_dqpurge_all(struct xfs_mount *, uint);
......
...@@ -91,8 +91,7 @@ xfs_qm_scall_quotaoff( ...@@ -91,8 +91,7 @@ xfs_qm_scall_quotaoff(
mutex_unlock(&q->qi_quotaofflock); mutex_unlock(&q->qi_quotaofflock);
/* XXX what to do if error ? Revert back to old vals incore ? */ /* XXX what to do if error ? Revert back to old vals incore ? */
error = xfs_qm_write_sb_changes(mp, XFS_SB_QFLAGS); return xfs_sync_sb(mp, false);
return error;
} }
dqtype = 0; dqtype = 0;
...@@ -313,7 +312,6 @@ xfs_qm_scall_quotaon( ...@@ -313,7 +312,6 @@ xfs_qm_scall_quotaon(
{ {
int error; int error;
uint qf; uint qf;
__int64_t sbflags;
flags &= (XFS_ALL_QUOTA_ACCT | XFS_ALL_QUOTA_ENFD); flags &= (XFS_ALL_QUOTA_ACCT | XFS_ALL_QUOTA_ENFD);
/* /*
...@@ -321,8 +319,6 @@ xfs_qm_scall_quotaon( ...@@ -321,8 +319,6 @@ xfs_qm_scall_quotaon(
*/ */
flags &= ~(XFS_ALL_QUOTA_ACCT); flags &= ~(XFS_ALL_QUOTA_ACCT);
sbflags = 0;
if (flags == 0) { if (flags == 0) {
xfs_debug(mp, "%s: zero flags, m_qflags=%x", xfs_debug(mp, "%s: zero flags, m_qflags=%x",
__func__, mp->m_qflags); __func__, mp->m_qflags);
...@@ -363,11 +359,11 @@ xfs_qm_scall_quotaon( ...@@ -363,11 +359,11 @@ xfs_qm_scall_quotaon(
/* /*
* There's nothing to change if it's the same. * There's nothing to change if it's the same.
*/ */
if ((qf & flags) == flags && sbflags == 0) if ((qf & flags) == flags)
return -EEXIST; return -EEXIST;
sbflags |= XFS_SB_QFLAGS;
if ((error = xfs_qm_write_sb_changes(mp, sbflags))) error = xfs_sync_sb(mp, false);
if (error)
return error; return error;
/* /*
* If we aren't trying to switch on quota enforcement, we are done. * If we aren't trying to switch on quota enforcement, we are done.
...@@ -774,7 +770,7 @@ xfs_qm_log_quotaoff( ...@@ -774,7 +770,7 @@ xfs_qm_log_quotaoff(
mp->m_sb.sb_qflags = (mp->m_qflags & ~(flags)) & XFS_MOUNT_QUOTA_ALL; mp->m_sb.sb_qflags = (mp->m_qflags & ~(flags)) & XFS_MOUNT_QUOTA_ALL;
spin_unlock(&mp->m_sb_lock); spin_unlock(&mp->m_sb_lock);
xfs_mod_sb(tp, XFS_SB_QFLAGS); xfs_log_sb(tp);
/* /*
* We have to make sure that the transaction is secure on disk before we * We have to make sure that the transaction is secure on disk before we
......
...@@ -685,7 +685,7 @@ xfs_blkdev_get( ...@@ -685,7 +685,7 @@ xfs_blkdev_get(
mp); mp);
if (IS_ERR(*bdevp)) { if (IS_ERR(*bdevp)) {
error = PTR_ERR(*bdevp); error = PTR_ERR(*bdevp);
xfs_warn(mp, "Invalid device [%s], error=%d\n", name, error); xfs_warn(mp, "Invalid device [%s], error=%d", name, error);
} }
return error; return error;
...@@ -1111,6 +1111,11 @@ xfs_fs_statfs( ...@@ -1111,6 +1111,11 @@ xfs_fs_statfs(
statp->f_files, statp->f_files,
mp->m_maxicount); mp->m_maxicount);
/* If sb_icount overshot maxicount, report actual allocation */
statp->f_files = max_t(typeof(statp->f_files),
statp->f_files,
sbp->sb_icount);
/* make sure statp->f_ffree does not underflow */ /* make sure statp->f_ffree does not underflow */
ffree = statp->f_files - (sbp->sb_icount - sbp->sb_ifree); ffree = statp->f_files - (sbp->sb_icount - sbp->sb_ifree);
statp->f_ffree = max_t(__int64_t, ffree, 0); statp->f_ffree = max_t(__int64_t, ffree, 0);
...@@ -1257,13 +1262,13 @@ xfs_fs_remount( ...@@ -1257,13 +1262,13 @@ xfs_fs_remount(
* If this is the first remount to writeable state we * If this is the first remount to writeable state we
* might have some superblock changes to update. * might have some superblock changes to update.
*/ */
if (mp->m_update_flags) { if (mp->m_update_sb) {
error = xfs_mount_log_sb(mp, mp->m_update_flags); error = xfs_sync_sb(mp, false);
if (error) { if (error) {
xfs_warn(mp, "failed to write sb changes"); xfs_warn(mp, "failed to write sb changes");
return error; return error;
} }
mp->m_update_flags = 0; mp->m_update_sb = false;
} }
/* /*
...@@ -1293,8 +1298,9 @@ xfs_fs_remount( ...@@ -1293,8 +1298,9 @@ xfs_fs_remount(
/* /*
* Second stage of a freeze. The data is already frozen so we only * Second stage of a freeze. The data is already frozen so we only
* need to take care of the metadata. Once that's done write a dummy * need to take care of the metadata. Once that's done sync the superblock
* record to dirty the log in case of a crash while frozen. * to the log to dirty it in case of a crash while frozen. This ensures that we
* will recover the unlinked inode lists on the next mount.
*/ */
STATIC int STATIC int
xfs_fs_freeze( xfs_fs_freeze(
...@@ -1304,7 +1310,7 @@ xfs_fs_freeze( ...@@ -1304,7 +1310,7 @@ xfs_fs_freeze(
xfs_save_resvblks(mp); xfs_save_resvblks(mp);
xfs_quiesce_attr(mp); xfs_quiesce_attr(mp);
return xfs_fs_log_dummy(mp); return xfs_sync_sb(mp, true);
} }
STATIC int STATIC int
......
...@@ -148,24 +148,6 @@ static struct ctl_table xfs_table[] = { ...@@ -148,24 +148,6 @@ static struct ctl_table xfs_table[] = {
.extra1 = &xfs_params.inherit_noatim.min, .extra1 = &xfs_params.inherit_noatim.min,
.extra2 = &xfs_params.inherit_noatim.max .extra2 = &xfs_params.inherit_noatim.max
}, },
{
.procname = "xfsbufd_centisecs",
.data = &xfs_params.xfs_buf_timer.val,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = &xfs_params.xfs_buf_timer.min,
.extra2 = &xfs_params.xfs_buf_timer.max
},
{
.procname = "age_buffer_centisecs",
.data = &xfs_params.xfs_buf_age.val,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = &xfs_params.xfs_buf_age.min,
.extra2 = &xfs_params.xfs_buf_age.max
},
{ {
.procname = "inherit_nosymlinks", .procname = "inherit_nosymlinks",
.data = &xfs_params.inherit_nosym.val, .data = &xfs_params.inherit_nosym.val,
......
...@@ -472,6 +472,7 @@ xfs_trans_apply_sb_deltas( ...@@ -472,6 +472,7 @@ xfs_trans_apply_sb_deltas(
whole = 1; whole = 1;
} }
xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SB_BUF);
if (whole) if (whole)
/* /*
* Log the whole thing, the fields are noncontiguous. * Log the whole thing, the fields are noncontiguous.
......
...@@ -327,9 +327,10 @@ xfs_trans_read_buf_map( ...@@ -327,9 +327,10 @@ xfs_trans_read_buf_map(
return -EIO; return -EIO;
} }
if (tp) if (tp) {
_xfs_trans_bjoin(tp, bp, 1); _xfs_trans_bjoin(tp, bp, 1);
trace_xfs_trans_read_buf(bp->b_fspriv); trace_xfs_trans_read_buf(bp->b_fspriv);
}
*bpp = bp; *bpp = bp;
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment