mirror of
https://github.com/joel16/android_kernel_sony_msm8994.git
synced 2024-11-28 22:51:06 +00:00
[XFS] Prevent ENOSPC from aborting transactions that need to succeed
During delayed allocation extent conversion or unwritten extent conversion, we need to reserve some blocks for transactions reservations. We need to reserve these blocks in case a btree split occurs and we need to allocate some blocks. Unfortunately, we've only ever reserved the number of data blocks we are allocating, so in both the unwritten and delalloc case we can get ENOSPC to the transaction reservation. This is bad because in both cases we cannot report the failure to the writing application. The fix is two-fold: 1 - leverage the reserved block infrastructure XFS already has to reserve a small pool of blocks by default to allow specially marked transactions to dip into when we are at ENOSPC. Default setting is min(5%, 1024 blocks). 2 - convert critical transaction reservations to be allowed to dip into this pool. Spots changed are delalloc conversion, unwritten extent conversion and growing a filesystem at ENOSPC. This also allows growing the filesytsem to succeed at ENOSPC. SGI-PV: 964468 SGI-Modid: xfs-linux-melb:xfs-kern:28865a Signed-off-by: David Chinner <dgc@sgi.com> Signed-off-by: Tim Shimmin <tes@sgi.com>
This commit is contained in:
parent
641c56fbfe
commit
84e1e99f11
@ -177,6 +177,7 @@ xfs_growfs_data_private(
|
|||||||
up_write(&mp->m_peraglock);
|
up_write(&mp->m_peraglock);
|
||||||
}
|
}
|
||||||
tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFS);
|
tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFS);
|
||||||
|
tp->t_flags |= XFS_TRANS_RESERVE;
|
||||||
if ((error = xfs_trans_reserve(tp, XFS_GROWFS_SPACE_RES(mp),
|
if ((error = xfs_trans_reserve(tp, XFS_GROWFS_SPACE_RES(mp),
|
||||||
XFS_GROWDATA_LOG_RES(mp), 0, 0, 0))) {
|
XFS_GROWDATA_LOG_RES(mp), 0, 0, 0))) {
|
||||||
xfs_trans_cancel(tp, 0);
|
xfs_trans_cancel(tp, 0);
|
||||||
@ -499,8 +500,9 @@ xfs_reserve_blocks(
|
|||||||
unsigned long s;
|
unsigned long s;
|
||||||
|
|
||||||
/* If inval is null, report current values and return */
|
/* If inval is null, report current values and return */
|
||||||
|
|
||||||
if (inval == (__uint64_t *)NULL) {
|
if (inval == (__uint64_t *)NULL) {
|
||||||
|
if (!outval)
|
||||||
|
return EINVAL;
|
||||||
outval->resblks = mp->m_resblks;
|
outval->resblks = mp->m_resblks;
|
||||||
outval->resblks_avail = mp->m_resblks_avail;
|
outval->resblks_avail = mp->m_resblks_avail;
|
||||||
return 0;
|
return 0;
|
||||||
@ -563,8 +565,10 @@ retry:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
|
if (outval) {
|
||||||
outval->resblks = mp->m_resblks;
|
outval->resblks = mp->m_resblks;
|
||||||
outval->resblks_avail = mp->m_resblks_avail;
|
outval->resblks_avail = mp->m_resblks_avail;
|
||||||
|
}
|
||||||
XFS_SB_UNLOCK(mp, s);
|
XFS_SB_UNLOCK(mp, s);
|
||||||
|
|
||||||
if (fdblks_delta) {
|
if (fdblks_delta) {
|
||||||
|
@ -788,18 +788,12 @@ xfs_iomap_write_allocate(
|
|||||||
nimaps = 0;
|
nimaps = 0;
|
||||||
while (nimaps == 0) {
|
while (nimaps == 0) {
|
||||||
tp = xfs_trans_alloc(mp, XFS_TRANS_STRAT_WRITE);
|
tp = xfs_trans_alloc(mp, XFS_TRANS_STRAT_WRITE);
|
||||||
|
tp->t_flags |= XFS_TRANS_RESERVE;
|
||||||
nres = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK);
|
nres = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK);
|
||||||
error = xfs_trans_reserve(tp, nres,
|
error = xfs_trans_reserve(tp, nres,
|
||||||
XFS_WRITE_LOG_RES(mp),
|
XFS_WRITE_LOG_RES(mp),
|
||||||
0, XFS_TRANS_PERM_LOG_RES,
|
0, XFS_TRANS_PERM_LOG_RES,
|
||||||
XFS_WRITE_LOG_COUNT);
|
XFS_WRITE_LOG_COUNT);
|
||||||
if (error == ENOSPC) {
|
|
||||||
error = xfs_trans_reserve(tp, 0,
|
|
||||||
XFS_WRITE_LOG_RES(mp),
|
|
||||||
0,
|
|
||||||
XFS_TRANS_PERM_LOG_RES,
|
|
||||||
XFS_WRITE_LOG_COUNT);
|
|
||||||
}
|
|
||||||
if (error) {
|
if (error) {
|
||||||
xfs_trans_cancel(tp, 0);
|
xfs_trans_cancel(tp, 0);
|
||||||
return XFS_ERROR(error);
|
return XFS_ERROR(error);
|
||||||
@ -917,8 +911,8 @@ xfs_iomap_write_unwritten(
|
|||||||
* from unwritten to real. Do allocations in a loop until
|
* from unwritten to real. Do allocations in a loop until
|
||||||
* we have covered the range passed in.
|
* we have covered the range passed in.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
tp = xfs_trans_alloc(mp, XFS_TRANS_STRAT_WRITE);
|
tp = xfs_trans_alloc(mp, XFS_TRANS_STRAT_WRITE);
|
||||||
|
tp->t_flags |= XFS_TRANS_RESERVE;
|
||||||
error = xfs_trans_reserve(tp, resblks,
|
error = xfs_trans_reserve(tp, resblks,
|
||||||
XFS_WRITE_LOG_RES(mp), 0,
|
XFS_WRITE_LOG_RES(mp), 0,
|
||||||
XFS_TRANS_PERM_LOG_RES,
|
XFS_TRANS_PERM_LOG_RES,
|
||||||
|
@ -725,7 +725,7 @@ xfs_mountfs(
|
|||||||
bhv_vnode_t *rvp = NULL;
|
bhv_vnode_t *rvp = NULL;
|
||||||
int readio_log, writeio_log;
|
int readio_log, writeio_log;
|
||||||
xfs_daddr_t d;
|
xfs_daddr_t d;
|
||||||
__uint64_t ret64;
|
__uint64_t resblks;
|
||||||
__int64_t update_flags;
|
__int64_t update_flags;
|
||||||
uint quotamount, quotaflags;
|
uint quotamount, quotaflags;
|
||||||
int agno;
|
int agno;
|
||||||
@ -842,6 +842,7 @@ xfs_mountfs(
|
|||||||
*/
|
*/
|
||||||
if ((mfsi_flags & XFS_MFSI_SECOND) == 0 &&
|
if ((mfsi_flags & XFS_MFSI_SECOND) == 0 &&
|
||||||
(mp->m_flags & XFS_MOUNT_NOUUID) == 0) {
|
(mp->m_flags & XFS_MOUNT_NOUUID) == 0) {
|
||||||
|
__uint64_t ret64;
|
||||||
if (xfs_uuid_mount(mp)) {
|
if (xfs_uuid_mount(mp)) {
|
||||||
error = XFS_ERROR(EINVAL);
|
error = XFS_ERROR(EINVAL);
|
||||||
goto error1;
|
goto error1;
|
||||||
@ -1135,13 +1136,27 @@ xfs_mountfs(
|
|||||||
goto error4;
|
goto error4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Complete the quota initialisation, post-log-replay component.
|
* Complete the quota initialisation, post-log-replay component.
|
||||||
*/
|
*/
|
||||||
if ((error = XFS_QM_MOUNT(mp, quotamount, quotaflags, mfsi_flags)))
|
if ((error = XFS_QM_MOUNT(mp, quotamount, quotaflags, mfsi_flags)))
|
||||||
goto error4;
|
goto error4;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now we are mounted, reserve a small amount of unused space for
|
||||||
|
* privileged transactions. This is needed so that transaction
|
||||||
|
* space required for critical operations can dip into this pool
|
||||||
|
* when at ENOSPC. This is needed for operations like create with
|
||||||
|
* attr, unwritten extent conversion at ENOSPC, etc. Data allocations
|
||||||
|
* are not allowed to use this reserved space.
|
||||||
|
*
|
||||||
|
* We default to 5% or 1024 fsbs of space reserved, whichever is smaller.
|
||||||
|
* This may drive us straight to ENOSPC on mount, but that implies
|
||||||
|
* we were already there on the last unmount.
|
||||||
|
*/
|
||||||
|
resblks = min_t(__uint64_t, mp->m_sb.sb_dblocks / 20, 1024);
|
||||||
|
xfs_reserve_blocks(mp, &resblks, NULL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error4:
|
error4:
|
||||||
@ -1181,6 +1196,7 @@ xfs_unmountfs(xfs_mount_t *mp, struct cred *cr)
|
|||||||
#if defined(DEBUG) || defined(INDUCE_IO_ERROR)
|
#if defined(DEBUG) || defined(INDUCE_IO_ERROR)
|
||||||
int64_t fsid;
|
int64_t fsid;
|
||||||
#endif
|
#endif
|
||||||
|
__uint64_t resblks;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We can potentially deadlock here if we have an inode cluster
|
* We can potentially deadlock here if we have an inode cluster
|
||||||
@ -1209,6 +1225,23 @@ xfs_unmountfs(xfs_mount_t *mp, struct cred *cr)
|
|||||||
xfs_binval(mp->m_rtdev_targp);
|
xfs_binval(mp->m_rtdev_targp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unreserve any blocks we have so that when we unmount we don't account
|
||||||
|
* the reserved free space as used. This is really only necessary for
|
||||||
|
* lazy superblock counting because it trusts the incore superblock
|
||||||
|
* counters to be aboslutely correct on clean unmount.
|
||||||
|
*
|
||||||
|
* We don't bother correcting this elsewhere for lazy superblock
|
||||||
|
* counting because on mount of an unclean filesystem we reconstruct the
|
||||||
|
* correct counter value and this is irrelevant.
|
||||||
|
*
|
||||||
|
* For non-lazy counter filesystems, this doesn't matter at all because
|
||||||
|
* we only every apply deltas to the superblock and hence the incore
|
||||||
|
* value does not matter....
|
||||||
|
*/
|
||||||
|
resblks = 0;
|
||||||
|
xfs_reserve_blocks(mp, &resblks, NULL);
|
||||||
|
|
||||||
xfs_log_sbcount(mp, 1);
|
xfs_log_sbcount(mp, 1);
|
||||||
xfs_unmountfs_writesb(mp);
|
xfs_unmountfs_writesb(mp);
|
||||||
xfs_unmountfs_wait(mp); /* wait for async bufs */
|
xfs_unmountfs_wait(mp); /* wait for async bufs */
|
||||||
|
Loading…
Reference in New Issue
Block a user