mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'xfs-merge-6.16' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux
Pull xfs updates from Carlos Maiolino: - Atomic writes for XFS - Remove experimental warnings for pNFS, scrub and parent pointers * tag 'xfs-merge-6.16' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux: (26 commits) xfs: add inode to zone caching for data placement xfs: free the item in xfs_mru_cache_insert on failure xfs: remove the EXPERIMENTAL warning for pNFS xfs: remove some EXPERIMENTAL warnings xfs: Remove deprecated xfs_bufd sysctl parameters xfs: stop using set_blocksize xfs: allow sysadmins to specify a maximum atomic write limit at mount time xfs: update atomic write limits xfs: add xfs_calc_atomic_write_unit_max() xfs: add xfs_file_dio_write_atomic() xfs: commit CoW-based atomic writes atomically xfs: add large atomic writes checks in xfs_direct_write_iomap_begin() xfs: add xfs_atomic_write_cow_iomap_begin() xfs: refine atomic write size check in xfs_file_write_iter() xfs: refactor xfs_reflink_end_cow_extent() xfs: allow block allocator to take an alignment hint xfs: ignore HW which cannot atomic write a single block xfs: add helpers to compute transaction reservation for finishing intent items xfs: add helpers to compute log item overhead xfs: separate out setting buftarg atomic writes limits ...
This commit is contained in:
@@ -151,6 +151,17 @@ When mounting an XFS filesystem, the following options are accepted.
|
||||
optional, and the log section can be separate from the data
|
||||
section or contained within it.
|
||||
|
||||
max_atomic_write=value
|
||||
Set the maximum size of an atomic write. The size may be
|
||||
specified in bytes, in kilobytes with a "k" suffix, in megabytes
|
||||
with a "m" suffix, or in gigabytes with a "g" suffix. The size
|
||||
cannot be larger than the maximum write size, larger than the
|
||||
size of any allocation group, or larger than the size of a
|
||||
remapping operation that the log can complete atomically.
|
||||
|
||||
The default value is to set the maximum I/O completion size
|
||||
to allow each CPU to handle one at a time.
|
||||
|
||||
max_open_zones=value
|
||||
Specify the max number of zones to keep open for writing on a
|
||||
zoned rt device. Many open zones aids file data separation
|
||||
|
||||
+2
-1
@@ -1335,7 +1335,8 @@ void bdev_statx(const struct path *path, struct kstat *stat, u32 request_mask)
|
||||
|
||||
generic_fill_statx_atomic_writes(stat,
|
||||
queue_atomic_write_unit_min_bytes(bd_queue),
|
||||
queue_atomic_write_unit_max_bytes(bd_queue));
|
||||
queue_atomic_write_unit_max_bytes(bd_queue),
|
||||
0);
|
||||
}
|
||||
|
||||
stat->blksize = bdev_io_min(bdev);
|
||||
|
||||
+1
-1
@@ -5692,7 +5692,7 @@ int ext4_getattr(struct mnt_idmap *idmap, const struct path *path,
|
||||
awu_max = sbi->s_awu_max;
|
||||
}
|
||||
|
||||
generic_fill_statx_atomic_writes(stat, awu_min, awu_max);
|
||||
generic_fill_statx_atomic_writes(stat, awu_min, awu_max, 0);
|
||||
}
|
||||
|
||||
flags = ei->i_flags & EXT4_FL_USER_VISIBLE;
|
||||
|
||||
@@ -136,13 +136,15 @@ EXPORT_SYMBOL(generic_fill_statx_attr);
|
||||
* @stat: Where to fill in the attribute flags
|
||||
* @unit_min: Minimum supported atomic write length in bytes
|
||||
* @unit_max: Maximum supported atomic write length in bytes
|
||||
* @unit_max_opt: Optimised maximum supported atomic write length in bytes
|
||||
*
|
||||
* Fill in the STATX{_ATTR}_WRITE_ATOMIC flags in the kstat structure from
|
||||
* atomic write unit_min and unit_max values.
|
||||
*/
|
||||
void generic_fill_statx_atomic_writes(struct kstat *stat,
|
||||
unsigned int unit_min,
|
||||
unsigned int unit_max)
|
||||
unsigned int unit_max,
|
||||
unsigned int unit_max_opt)
|
||||
{
|
||||
/* Confirm that the request type is known */
|
||||
stat->result_mask |= STATX_WRITE_ATOMIC;
|
||||
@@ -153,6 +155,7 @@ void generic_fill_statx_atomic_writes(struct kstat *stat,
|
||||
if (unit_min) {
|
||||
stat->atomic_write_unit_min = unit_min;
|
||||
stat->atomic_write_unit_max = unit_max;
|
||||
stat->atomic_write_unit_max_opt = unit_max_opt;
|
||||
/* Initially only allow 1x segment */
|
||||
stat->atomic_write_segments_max = 1;
|
||||
|
||||
@@ -741,6 +744,7 @@ cp_statx(const struct kstat *stat, struct statx __user *buffer)
|
||||
tmp.stx_atomic_write_unit_min = stat->atomic_write_unit_min;
|
||||
tmp.stx_atomic_write_unit_max = stat->atomic_write_unit_max;
|
||||
tmp.stx_atomic_write_segments_max = stat->atomic_write_segments_max;
|
||||
tmp.stx_atomic_write_unit_max_opt = stat->atomic_write_unit_max_opt;
|
||||
|
||||
return copy_to_user(buffer, &tmp, sizeof(tmp)) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
@@ -3312,6 +3312,11 @@ xfs_bmap_compute_alignments(
|
||||
align = xfs_get_cowextsz_hint(ap->ip);
|
||||
else if (ap->datatype & XFS_ALLOC_USERDATA)
|
||||
align = xfs_get_extsz_hint(ap->ip);
|
||||
|
||||
/* Try to align start block to any minimum allocation alignment */
|
||||
if (align > 1 && (ap->flags & XFS_BMAPI_EXTSZALIGN))
|
||||
args->alignment = align;
|
||||
|
||||
if (align) {
|
||||
if (xfs_bmap_extsize_align(mp, &ap->got, &ap->prev, align, 0,
|
||||
ap->eof, 0, ap->conv, &ap->offset,
|
||||
|
||||
@@ -87,6 +87,9 @@ struct xfs_bmalloca {
|
||||
/* Do not update the rmap btree. Used for reconstructing bmbt from rmapbt. */
|
||||
#define XFS_BMAPI_NORMAP (1u << 10)
|
||||
|
||||
/* Try to align allocations to the extent size hint */
|
||||
#define XFS_BMAPI_EXTSZALIGN (1u << 11)
|
||||
|
||||
#define XFS_BMAPI_FLAGS \
|
||||
{ XFS_BMAPI_ENTIRE, "ENTIRE" }, \
|
||||
{ XFS_BMAPI_METADATA, "METADATA" }, \
|
||||
@@ -98,7 +101,8 @@ struct xfs_bmalloca {
|
||||
{ XFS_BMAPI_REMAP, "REMAP" }, \
|
||||
{ XFS_BMAPI_COWFORK, "COWFORK" }, \
|
||||
{ XFS_BMAPI_NODISCARD, "NODISCARD" }, \
|
||||
{ XFS_BMAPI_NORMAP, "NORMAP" }
|
||||
{ XFS_BMAPI_NORMAP, "NORMAP" },\
|
||||
{ XFS_BMAPI_EXTSZALIGN, "EXTSZALIGN" }
|
||||
|
||||
|
||||
static inline int xfs_bmapi_aflag(int w)
|
||||
|
||||
@@ -91,6 +91,7 @@ xfs_log_calc_trans_resv_for_minlogblocks(
|
||||
*/
|
||||
if (xfs_want_minlogsize_fixes(&mp->m_sb)) {
|
||||
xfs_trans_resv_calc(mp, resv);
|
||||
resv->tr_atomic_ioend = M_RES(mp)->tr_atomic_ioend;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -107,6 +108,9 @@ xfs_log_calc_trans_resv_for_minlogblocks(
|
||||
|
||||
xfs_trans_resv_calc(mp, resv);
|
||||
|
||||
/* Copy the dynamic transaction reservation types from the running fs */
|
||||
resv->tr_atomic_ioend = M_RES(mp)->tr_atomic_ioend;
|
||||
|
||||
if (xfs_has_reflink(mp)) {
|
||||
/*
|
||||
* In the early days of reflink, typical log operation counts
|
||||
|
||||
+312
-31
@@ -22,6 +22,12 @@
|
||||
#include "xfs_rtbitmap.h"
|
||||
#include "xfs_attr_item.h"
|
||||
#include "xfs_log.h"
|
||||
#include "xfs_defer.h"
|
||||
#include "xfs_bmap_item.h"
|
||||
#include "xfs_extfree_item.h"
|
||||
#include "xfs_rmap_item.h"
|
||||
#include "xfs_refcount_item.h"
|
||||
#include "xfs_trace.h"
|
||||
|
||||
#define _ALLOC true
|
||||
#define _FREE false
|
||||
@@ -263,6 +269,42 @@ xfs_rtalloc_block_count(
|
||||
* register overflow from temporaries in the calculations.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Finishing a data device refcount updates (t1):
|
||||
* the agfs of the ags containing the blocks: nr_ops * sector size
|
||||
* the refcount btrees: nr_ops * 1 trees * (2 * max depth - 1) * block size
|
||||
*/
|
||||
inline unsigned int
|
||||
xfs_calc_finish_cui_reservation(
|
||||
struct xfs_mount *mp,
|
||||
unsigned int nr_ops)
|
||||
{
|
||||
if (!xfs_has_reflink(mp))
|
||||
return 0;
|
||||
|
||||
return xfs_calc_buf_res(nr_ops, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(xfs_refcountbt_block_count(mp, nr_ops),
|
||||
mp->m_sb.sb_blocksize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Realtime refcount updates (t2);
|
||||
* the rt refcount inode
|
||||
* the rtrefcount btrees: nr_ops * 1 trees * (2 * max depth - 1) * block size
|
||||
*/
|
||||
inline unsigned int
|
||||
xfs_calc_finish_rt_cui_reservation(
|
||||
struct xfs_mount *mp,
|
||||
unsigned int nr_ops)
|
||||
{
|
||||
if (!xfs_has_rtreflink(mp))
|
||||
return 0;
|
||||
|
||||
return xfs_calc_inode_res(mp, 1) +
|
||||
xfs_calc_buf_res(xfs_rtrefcountbt_block_count(mp, nr_ops),
|
||||
mp->m_sb.sb_blocksize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the log reservation required to handle the refcount update
|
||||
* transaction. Refcount updates are always done via deferred log items.
|
||||
@@ -280,19 +322,10 @@ xfs_calc_refcountbt_reservation(
|
||||
struct xfs_mount *mp,
|
||||
unsigned int nr_ops)
|
||||
{
|
||||
unsigned int blksz = XFS_FSB_TO_B(mp, 1);
|
||||
unsigned int t1, t2 = 0;
|
||||
unsigned int t1, t2;
|
||||
|
||||
if (!xfs_has_reflink(mp))
|
||||
return 0;
|
||||
|
||||
t1 = xfs_calc_buf_res(nr_ops, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(xfs_refcountbt_block_count(mp, nr_ops), blksz);
|
||||
|
||||
if (xfs_has_realtime(mp))
|
||||
t2 = xfs_calc_inode_res(mp, 1) +
|
||||
xfs_calc_buf_res(xfs_rtrefcountbt_block_count(mp, nr_ops),
|
||||
blksz);
|
||||
t1 = xfs_calc_finish_cui_reservation(mp, nr_ops);
|
||||
t2 = xfs_calc_finish_rt_cui_reservation(mp, nr_ops);
|
||||
|
||||
return max(t1, t2);
|
||||
}
|
||||
@@ -379,6 +412,96 @@ xfs_calc_write_reservation_minlogsize(
|
||||
return xfs_calc_write_reservation(mp, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finishing an EFI can free the blocks and bmap blocks (t2):
|
||||
* the agf for each of the ags: nr * sector size
|
||||
* the agfl for each of the ags: nr * sector size
|
||||
* the super block to reflect the freed blocks: sector size
|
||||
* worst case split in allocation btrees per extent assuming nr extents:
|
||||
* nr exts * 2 trees * (2 * max depth - 1) * block size
|
||||
*/
|
||||
inline unsigned int
|
||||
xfs_calc_finish_efi_reservation(
|
||||
struct xfs_mount *mp,
|
||||
unsigned int nr)
|
||||
{
|
||||
return xfs_calc_buf_res((2 * nr) + 1, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(xfs_allocfree_block_count(mp, nr),
|
||||
mp->m_sb.sb_blocksize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Or, if it's a realtime file (t3):
|
||||
* the agf for each of the ags: 2 * sector size
|
||||
* the agfl for each of the ags: 2 * sector size
|
||||
* the super block to reflect the freed blocks: sector size
|
||||
* the realtime bitmap:
|
||||
* 2 exts * ((XFS_BMBT_MAX_EXTLEN / rtextsize) / NBBY) bytes
|
||||
* the realtime summary: 2 exts * 1 block
|
||||
* worst case split in allocation btrees per extent assuming 2 extents:
|
||||
* 2 exts * 2 trees * (2 * max depth - 1) * block size
|
||||
*/
|
||||
inline unsigned int
|
||||
xfs_calc_finish_rt_efi_reservation(
|
||||
struct xfs_mount *mp,
|
||||
unsigned int nr)
|
||||
{
|
||||
if (!xfs_has_realtime(mp))
|
||||
return 0;
|
||||
|
||||
return xfs_calc_buf_res((2 * nr) + 1, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(xfs_rtalloc_block_count(mp, nr),
|
||||
mp->m_sb.sb_blocksize) +
|
||||
xfs_calc_buf_res(xfs_allocfree_block_count(mp, nr),
|
||||
mp->m_sb.sb_blocksize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finishing an RUI is the same as an EFI. We can split the rmap btree twice
|
||||
* on each end of the record, and that can cause the AGFL to be refilled or
|
||||
* emptied out.
|
||||
*/
|
||||
inline unsigned int
|
||||
xfs_calc_finish_rui_reservation(
|
||||
struct xfs_mount *mp,
|
||||
unsigned int nr)
|
||||
{
|
||||
if (!xfs_has_rmapbt(mp))
|
||||
return 0;
|
||||
return xfs_calc_finish_efi_reservation(mp, nr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finishing an RUI is the same as an EFI. We can split the rmap btree twice
|
||||
* on each end of the record, and that can cause the AGFL to be refilled or
|
||||
* emptied out.
|
||||
*/
|
||||
inline unsigned int
|
||||
xfs_calc_finish_rt_rui_reservation(
|
||||
struct xfs_mount *mp,
|
||||
unsigned int nr)
|
||||
{
|
||||
if (!xfs_has_rtrmapbt(mp))
|
||||
return 0;
|
||||
return xfs_calc_finish_rt_efi_reservation(mp, nr);
|
||||
}
|
||||
|
||||
/*
|
||||
* In finishing a BUI, we can modify:
|
||||
* the inode being truncated: inode size
|
||||
* dquots
|
||||
* the inode's bmap btree: (max depth + 1) * block size
|
||||
*/
|
||||
inline unsigned int
|
||||
xfs_calc_finish_bui_reservation(
|
||||
struct xfs_mount *mp,
|
||||
unsigned int nr)
|
||||
{
|
||||
return xfs_calc_inode_res(mp, 1) + XFS_DQUOT_LOGRES +
|
||||
xfs_calc_buf_res(XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) + 1,
|
||||
mp->m_sb.sb_blocksize);
|
||||
}
|
||||
|
||||
/*
|
||||
* In truncating a file we free up to two extents at once. We can modify (t1):
|
||||
* the inode being truncated: inode size
|
||||
@@ -411,16 +534,8 @@ xfs_calc_itruncate_reservation(
|
||||
t1 = xfs_calc_inode_res(mp, 1) +
|
||||
xfs_calc_buf_res(XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) + 1, blksz);
|
||||
|
||||
t2 = xfs_calc_buf_res(9, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(xfs_allocfree_block_count(mp, 4), blksz);
|
||||
|
||||
if (xfs_has_realtime(mp)) {
|
||||
t3 = xfs_calc_buf_res(5, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(xfs_rtalloc_block_count(mp, 2), blksz) +
|
||||
xfs_calc_buf_res(xfs_allocfree_block_count(mp, 2), blksz);
|
||||
} else {
|
||||
t3 = 0;
|
||||
}
|
||||
t2 = xfs_calc_finish_efi_reservation(mp, 4);
|
||||
t3 = xfs_calc_finish_rt_efi_reservation(mp, 2);
|
||||
|
||||
/*
|
||||
* In the early days of reflink, we included enough reservation to log
|
||||
@@ -501,9 +616,7 @@ xfs_calc_rename_reservation(
|
||||
xfs_calc_buf_res(2 * XFS_DIROP_LOG_COUNT(mp),
|
||||
XFS_FSB_TO_B(mp, 1));
|
||||
|
||||
t2 = xfs_calc_buf_res(7, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(xfs_allocfree_block_count(mp, 3),
|
||||
XFS_FSB_TO_B(mp, 1));
|
||||
t2 = xfs_calc_finish_efi_reservation(mp, 3);
|
||||
|
||||
if (xfs_has_parent(mp)) {
|
||||
unsigned int rename_overhead, exchange_overhead;
|
||||
@@ -611,9 +724,7 @@ xfs_calc_link_reservation(
|
||||
overhead += xfs_calc_iunlink_remove_reservation(mp);
|
||||
t1 = xfs_calc_inode_res(mp, 2) +
|
||||
xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp), XFS_FSB_TO_B(mp, 1));
|
||||
t2 = xfs_calc_buf_res(3, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(xfs_allocfree_block_count(mp, 1),
|
||||
XFS_FSB_TO_B(mp, 1));
|
||||
t2 = xfs_calc_finish_efi_reservation(mp, 1);
|
||||
|
||||
if (xfs_has_parent(mp)) {
|
||||
t3 = resp->tr_attrsetm.tr_logres;
|
||||
@@ -676,9 +787,7 @@ xfs_calc_remove_reservation(
|
||||
|
||||
t1 = xfs_calc_inode_res(mp, 2) +
|
||||
xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp), XFS_FSB_TO_B(mp, 1));
|
||||
t2 = xfs_calc_buf_res(4, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(xfs_allocfree_block_count(mp, 2),
|
||||
XFS_FSB_TO_B(mp, 1));
|
||||
t2 = xfs_calc_finish_efi_reservation(mp, 2);
|
||||
|
||||
if (xfs_has_parent(mp)) {
|
||||
t3 = resp->tr_attrrm.tr_logres;
|
||||
@@ -1181,6 +1290,15 @@ xfs_calc_namespace_reservations(
|
||||
resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_calc_default_atomic_ioend_reservation(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_trans_resv *resp)
|
||||
{
|
||||
/* Pick a default that will scale reasonably for the log size. */
|
||||
resp->tr_atomic_ioend = resp->tr_itruncate;
|
||||
}
|
||||
|
||||
void
|
||||
xfs_trans_resv_calc(
|
||||
struct xfs_mount *mp,
|
||||
@@ -1275,4 +1393,167 @@ xfs_trans_resv_calc(
|
||||
resp->tr_itruncate.tr_logcount += logcount_adj;
|
||||
resp->tr_write.tr_logcount += logcount_adj;
|
||||
resp->tr_qm_dqalloc.tr_logcount += logcount_adj;
|
||||
|
||||
/*
|
||||
* Now that we've finished computing the static reservations, we can
|
||||
* compute the dynamic reservation for atomic writes.
|
||||
*/
|
||||
xfs_calc_default_atomic_ioend_reservation(mp, resp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the per-extent and fixed transaction reservation sizes needed to
|
||||
* complete an atomic write.
|
||||
*/
|
||||
STATIC unsigned int
|
||||
xfs_calc_atomic_write_ioend_geometry(
|
||||
struct xfs_mount *mp,
|
||||
unsigned int *step_size)
|
||||
{
|
||||
const unsigned int efi = xfs_efi_log_space(1);
|
||||
const unsigned int efd = xfs_efd_log_space(1);
|
||||
const unsigned int rui = xfs_rui_log_space(1);
|
||||
const unsigned int rud = xfs_rud_log_space();
|
||||
const unsigned int cui = xfs_cui_log_space(1);
|
||||
const unsigned int cud = xfs_cud_log_space();
|
||||
const unsigned int bui = xfs_bui_log_space(1);
|
||||
const unsigned int bud = xfs_bud_log_space();
|
||||
|
||||
/*
|
||||
* Maximum overhead to complete an atomic write ioend in software:
|
||||
* remove data fork extent + remove cow fork extent + map extent into
|
||||
* data fork.
|
||||
*
|
||||
* tx0: Creates a BUI and a CUI and that's all it needs.
|
||||
*
|
||||
* tx1: Roll to finish the BUI. Need space for the BUD, an RUI, and
|
||||
* enough space to relog the CUI (== CUI + CUD).
|
||||
*
|
||||
* tx2: Roll again to finish the RUI. Need space for the RUD and space
|
||||
* to relog the CUI.
|
||||
*
|
||||
* tx3: Roll again, need space for the CUD and possibly a new EFI.
|
||||
*
|
||||
* tx4: Roll again, need space for an EFD.
|
||||
*
|
||||
* If the extent referenced by the pair of BUI/CUI items is not the one
|
||||
* being currently processed, then we need to reserve space to relog
|
||||
* both items.
|
||||
*/
|
||||
const unsigned int tx0 = bui + cui;
|
||||
const unsigned int tx1 = bud + rui + cui + cud;
|
||||
const unsigned int tx2 = rud + cui + cud;
|
||||
const unsigned int tx3 = cud + efi;
|
||||
const unsigned int tx4 = efd;
|
||||
const unsigned int relog = bui + bud + cui + cud;
|
||||
|
||||
const unsigned int per_intent = max(max3(tx0, tx1, tx2),
|
||||
max3(tx3, tx4, relog));
|
||||
|
||||
/* Overhead to finish one step of each intent item type */
|
||||
const unsigned int f1 = xfs_calc_finish_efi_reservation(mp, 1);
|
||||
const unsigned int f2 = xfs_calc_finish_rui_reservation(mp, 1);
|
||||
const unsigned int f3 = xfs_calc_finish_cui_reservation(mp, 1);
|
||||
const unsigned int f4 = xfs_calc_finish_bui_reservation(mp, 1);
|
||||
|
||||
/* We only finish one item per transaction in a chain */
|
||||
*step_size = max(f4, max3(f1, f2, f3));
|
||||
|
||||
return per_intent;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the maximum size (in fsblocks) of atomic writes that we can complete
|
||||
* given the existing log reservations.
|
||||
*/
|
||||
xfs_extlen_t
|
||||
xfs_calc_max_atomic_write_fsblocks(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
const struct xfs_trans_res *resv = &M_RES(mp)->tr_atomic_ioend;
|
||||
unsigned int per_intent = 0;
|
||||
unsigned int step_size = 0;
|
||||
unsigned int ret = 0;
|
||||
|
||||
if (resv->tr_logres > 0) {
|
||||
per_intent = xfs_calc_atomic_write_ioend_geometry(mp,
|
||||
&step_size);
|
||||
|
||||
if (resv->tr_logres >= step_size)
|
||||
ret = (resv->tr_logres - step_size) / per_intent;
|
||||
}
|
||||
|
||||
trace_xfs_calc_max_atomic_write_fsblocks(mp, per_intent, step_size,
|
||||
resv->tr_logres, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the log blocks and transaction reservation needed to complete an
|
||||
* atomic write of a given number of blocks. Worst case, each block requires
|
||||
* separate handling. A return value of 0 means something went wrong.
|
||||
*/
|
||||
xfs_extlen_t
|
||||
xfs_calc_atomic_write_log_geometry(
|
||||
struct xfs_mount *mp,
|
||||
xfs_extlen_t blockcount,
|
||||
unsigned int *new_logres)
|
||||
{
|
||||
struct xfs_trans_res *curr_res = &M_RES(mp)->tr_atomic_ioend;
|
||||
uint old_logres = curr_res->tr_logres;
|
||||
unsigned int per_intent, step_size;
|
||||
unsigned int logres;
|
||||
xfs_extlen_t min_logblocks;
|
||||
|
||||
ASSERT(blockcount > 0);
|
||||
|
||||
xfs_calc_default_atomic_ioend_reservation(mp, M_RES(mp));
|
||||
|
||||
per_intent = xfs_calc_atomic_write_ioend_geometry(mp, &step_size);
|
||||
|
||||
/* Check for overflows */
|
||||
if (check_mul_overflow(blockcount, per_intent, &logres) ||
|
||||
check_add_overflow(logres, step_size, &logres))
|
||||
return 0;
|
||||
|
||||
curr_res->tr_logres = logres;
|
||||
min_logblocks = xfs_log_calc_minimum_size(mp);
|
||||
curr_res->tr_logres = old_logres;
|
||||
|
||||
trace_xfs_calc_max_atomic_write_log_geometry(mp, per_intent, step_size,
|
||||
blockcount, min_logblocks, logres);
|
||||
|
||||
*new_logres = logres;
|
||||
return min_logblocks;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the transaction reservation needed to complete an out of place
|
||||
* atomic write of a given number of blocks.
|
||||
*/
|
||||
int
|
||||
xfs_calc_atomic_write_reservation(
|
||||
struct xfs_mount *mp,
|
||||
xfs_extlen_t blockcount)
|
||||
{
|
||||
unsigned int new_logres;
|
||||
xfs_extlen_t min_logblocks;
|
||||
|
||||
/*
|
||||
* If the caller doesn't ask for a specific atomic write size, then
|
||||
* use the defaults.
|
||||
*/
|
||||
if (blockcount == 0) {
|
||||
xfs_calc_default_atomic_ioend_reservation(mp, M_RES(mp));
|
||||
return 0;
|
||||
}
|
||||
|
||||
min_logblocks = xfs_calc_atomic_write_log_geometry(mp, blockcount,
|
||||
&new_logres);
|
||||
if (!min_logblocks || min_logblocks > mp->m_sb.sb_logblocks)
|
||||
return -EINVAL;
|
||||
|
||||
M_RES(mp)->tr_atomic_ioend.tr_logres = new_logres;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ struct xfs_trans_resv {
|
||||
struct xfs_trans_res tr_qm_dqalloc; /* allocate quota on disk */
|
||||
struct xfs_trans_res tr_sb; /* modify superblock */
|
||||
struct xfs_trans_res tr_fsyncts; /* update timestamps on fsync */
|
||||
struct xfs_trans_res tr_atomic_ioend; /* untorn write completion */
|
||||
};
|
||||
|
||||
/* shorthand way of accessing reservation structure */
|
||||
@@ -98,8 +99,32 @@ struct xfs_trans_resv {
|
||||
void xfs_trans_resv_calc(struct xfs_mount *mp, struct xfs_trans_resv *resp);
|
||||
uint xfs_allocfree_block_count(struct xfs_mount *mp, uint num_ops);
|
||||
|
||||
unsigned int xfs_calc_finish_bui_reservation(struct xfs_mount *mp,
|
||||
unsigned int nr_ops);
|
||||
|
||||
unsigned int xfs_calc_finish_efi_reservation(struct xfs_mount *mp,
|
||||
unsigned int nr_ops);
|
||||
unsigned int xfs_calc_finish_rt_efi_reservation(struct xfs_mount *mp,
|
||||
unsigned int nr_ops);
|
||||
|
||||
unsigned int xfs_calc_finish_rui_reservation(struct xfs_mount *mp,
|
||||
unsigned int nr_ops);
|
||||
unsigned int xfs_calc_finish_rt_rui_reservation(struct xfs_mount *mp,
|
||||
unsigned int nr_ops);
|
||||
|
||||
unsigned int xfs_calc_finish_cui_reservation(struct xfs_mount *mp,
|
||||
unsigned int nr_ops);
|
||||
unsigned int xfs_calc_finish_rt_cui_reservation(struct xfs_mount *mp,
|
||||
unsigned int nr_ops);
|
||||
|
||||
unsigned int xfs_calc_itruncate_reservation_minlogsize(struct xfs_mount *mp);
|
||||
unsigned int xfs_calc_write_reservation_minlogsize(struct xfs_mount *mp);
|
||||
unsigned int xfs_calc_qm_dqalloc_reservation_minlogsize(struct xfs_mount *mp);
|
||||
|
||||
xfs_extlen_t xfs_calc_max_atomic_write_fsblocks(struct xfs_mount *mp);
|
||||
xfs_extlen_t xfs_calc_atomic_write_log_geometry(struct xfs_mount *mp,
|
||||
xfs_extlen_t blockcount, unsigned int *new_logres);
|
||||
int xfs_calc_atomic_write_reservation(struct xfs_mount *mp,
|
||||
xfs_extlen_t blockcount);
|
||||
|
||||
#endif /* __XFS_TRANS_RESV_H__ */
|
||||
|
||||
@@ -680,8 +680,6 @@ xfs_scrub_metadata(
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
xfs_warn_experimental(mp, XFS_EXPERIMENTAL_SCRUB);
|
||||
|
||||
sc = kzalloc(sizeof(struct xfs_scrub), XCHK_GFP_FLAGS);
|
||||
if (!sc) {
|
||||
error = -ENOMEM;
|
||||
|
||||
@@ -77,6 +77,11 @@ xfs_bui_item_size(
|
||||
*nbytes += xfs_bui_log_format_sizeof(buip->bui_format.bui_nextents);
|
||||
}
|
||||
|
||||
unsigned int xfs_bui_log_space(unsigned int nr)
|
||||
{
|
||||
return xlog_item_space(1, xfs_bui_log_format_sizeof(nr));
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called to fill in the vector of log iovecs for the
|
||||
* given bui log item. We use only 1 iovec, and we point that
|
||||
@@ -168,6 +173,11 @@ xfs_bud_item_size(
|
||||
*nbytes += sizeof(struct xfs_bud_log_format);
|
||||
}
|
||||
|
||||
unsigned int xfs_bud_log_space(void)
|
||||
{
|
||||
return xlog_item_space(1, sizeof(struct xfs_bud_log_format));
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called to fill in the vector of log iovecs for the
|
||||
* given bud log item. We use only 1 iovec, and we point that
|
||||
|
||||
@@ -72,4 +72,7 @@ struct xfs_bmap_intent;
|
||||
|
||||
void xfs_bmap_defer_add(struct xfs_trans *tp, struct xfs_bmap_intent *bi);
|
||||
|
||||
unsigned int xfs_bui_log_space(unsigned int nr);
|
||||
unsigned int xfs_bud_log_space(void);
|
||||
|
||||
#endif /* __XFS_BMAP_ITEM_H__ */
|
||||
|
||||
+63
-16
@@ -1687,23 +1687,65 @@ xfs_free_buftarg(
|
||||
kfree(btp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure this buffer target for hardware-assisted atomic writes if the
|
||||
* underlying block device supports is congruent with the filesystem geometry.
|
||||
*/
|
||||
static inline void
|
||||
xfs_configure_buftarg_atomic_writes(
|
||||
struct xfs_buftarg *btp)
|
||||
{
|
||||
struct xfs_mount *mp = btp->bt_mount;
|
||||
unsigned int min_bytes, max_bytes;
|
||||
|
||||
min_bytes = bdev_atomic_write_unit_min_bytes(btp->bt_bdev);
|
||||
max_bytes = bdev_atomic_write_unit_max_bytes(btp->bt_bdev);
|
||||
|
||||
/*
|
||||
* Ignore atomic write geometry that is nonsense or doesn't even cover
|
||||
* a single fsblock.
|
||||
*/
|
||||
if (min_bytes > max_bytes ||
|
||||
min_bytes > mp->m_sb.sb_blocksize ||
|
||||
max_bytes < mp->m_sb.sb_blocksize) {
|
||||
min_bytes = 0;
|
||||
max_bytes = 0;
|
||||
}
|
||||
|
||||
btp->bt_bdev_awu_min = min_bytes;
|
||||
btp->bt_bdev_awu_max = max_bytes;
|
||||
}
|
||||
|
||||
/* Configure a buffer target that abstracts a block device. */
|
||||
int
|
||||
xfs_setsize_buftarg(
|
||||
xfs_configure_buftarg(
|
||||
struct xfs_buftarg *btp,
|
||||
unsigned int sectorsize)
|
||||
{
|
||||
int error;
|
||||
|
||||
ASSERT(btp->bt_bdev != NULL);
|
||||
|
||||
/* Set up metadata sector size info */
|
||||
btp->bt_meta_sectorsize = sectorsize;
|
||||
btp->bt_meta_sectormask = sectorsize - 1;
|
||||
|
||||
if (set_blocksize(btp->bt_bdev_file, sectorsize)) {
|
||||
error = bdev_validate_blocksize(btp->bt_bdev, sectorsize);
|
||||
if (error) {
|
||||
xfs_warn(btp->bt_mount,
|
||||
"Cannot set_blocksize to %u on device %pg",
|
||||
sectorsize, btp->bt_bdev);
|
||||
"Cannot use blocksize %u on device %pg, err %d",
|
||||
sectorsize, btp->bt_bdev, error);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
/*
|
||||
* Flush the block device pagecache so our bios see anything dirtied
|
||||
* before mount.
|
||||
*/
|
||||
if (bdev_can_atomic_write(btp->bt_bdev))
|
||||
xfs_configure_buftarg_atomic_writes(btp);
|
||||
|
||||
return sync_blockdev(btp->bt_bdev);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -1752,6 +1794,8 @@ xfs_alloc_buftarg(
|
||||
{
|
||||
struct xfs_buftarg *btp;
|
||||
const struct dax_holder_operations *ops = NULL;
|
||||
int error;
|
||||
|
||||
|
||||
#if defined(CONFIG_FS_DAX) && defined(CONFIG_MEMORY_FAILURE)
|
||||
ops = &xfs_dax_holder_operations;
|
||||
@@ -1765,28 +1809,31 @@ xfs_alloc_buftarg(
|
||||
btp->bt_daxdev = fs_dax_get_by_bdev(btp->bt_bdev, &btp->bt_dax_part_off,
|
||||
mp, ops);
|
||||
|
||||
if (bdev_can_atomic_write(btp->bt_bdev)) {
|
||||
btp->bt_bdev_awu_min = bdev_atomic_write_unit_min_bytes(
|
||||
btp->bt_bdev);
|
||||
btp->bt_bdev_awu_max = bdev_atomic_write_unit_max_bytes(
|
||||
btp->bt_bdev);
|
||||
}
|
||||
/*
|
||||
* Flush and invalidate all devices' pagecaches before reading any
|
||||
* metadata because XFS doesn't use the bdev pagecache.
|
||||
*/
|
||||
error = sync_blockdev(btp->bt_bdev);
|
||||
if (error)
|
||||
goto error_free;
|
||||
|
||||
/*
|
||||
* When allocating the buftargs we have not yet read the super block and
|
||||
* thus don't know the file system sector size yet.
|
||||
*/
|
||||
if (xfs_setsize_buftarg(btp, bdev_logical_block_size(btp->bt_bdev)))
|
||||
goto error_free;
|
||||
if (xfs_init_buftarg(btp, bdev_logical_block_size(btp->bt_bdev),
|
||||
mp->m_super->s_id))
|
||||
btp->bt_meta_sectorsize = bdev_logical_block_size(btp->bt_bdev);
|
||||
btp->bt_meta_sectormask = btp->bt_meta_sectorsize - 1;
|
||||
|
||||
error = xfs_init_buftarg(btp, btp->bt_meta_sectorsize,
|
||||
mp->m_super->s_id);
|
||||
if (error)
|
||||
goto error_free;
|
||||
|
||||
return btp;
|
||||
|
||||
error_free:
|
||||
kfree(btp);
|
||||
return NULL;
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
||||
+2
-2
@@ -112,7 +112,7 @@ struct xfs_buftarg {
|
||||
struct percpu_counter bt_readahead_count;
|
||||
struct ratelimit_state bt_ioerror_rl;
|
||||
|
||||
/* Atomic write unit values */
|
||||
/* Atomic write unit values, bytes */
|
||||
unsigned int bt_bdev_awu_min;
|
||||
unsigned int bt_bdev_awu_max;
|
||||
|
||||
@@ -374,7 +374,7 @@ struct xfs_buftarg *xfs_alloc_buftarg(struct xfs_mount *mp,
|
||||
extern void xfs_free_buftarg(struct xfs_buftarg *);
|
||||
extern void xfs_buftarg_wait(struct xfs_buftarg *);
|
||||
extern void xfs_buftarg_drain(struct xfs_buftarg *);
|
||||
extern int xfs_setsize_buftarg(struct xfs_buftarg *, unsigned int);
|
||||
int xfs_configure_buftarg(struct xfs_buftarg *btp, unsigned int sectorsize);
|
||||
|
||||
#define xfs_getsize_buftarg(buftarg) block_size((buftarg)->bt_bdev)
|
||||
#define xfs_readonly_buftarg(buftarg) bdev_read_only((buftarg)->bt_bdev)
|
||||
|
||||
@@ -103,6 +103,25 @@ xfs_buf_item_size_segment(
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the worst case log item overhead for an invalidated buffer with the
|
||||
* given map count and block size.
|
||||
*/
|
||||
unsigned int
|
||||
xfs_buf_inval_log_space(
|
||||
unsigned int map_count,
|
||||
unsigned int blocksize)
|
||||
{
|
||||
unsigned int chunks = DIV_ROUND_UP(blocksize, XFS_BLF_CHUNK);
|
||||
unsigned int bitmap_size = DIV_ROUND_UP(chunks, NBWORD);
|
||||
unsigned int ret =
|
||||
offsetof(struct xfs_buf_log_format, blf_data_map) +
|
||||
(bitmap_size * sizeof_field(struct xfs_buf_log_format,
|
||||
blf_data_map[0]));
|
||||
|
||||
return ret * map_count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of log iovecs and space needed to log the given buf log
|
||||
* item.
|
||||
|
||||
@@ -64,6 +64,9 @@ static inline void xfs_buf_dquot_iodone(struct xfs_buf *bp)
|
||||
void xfs_buf_iodone(struct xfs_buf *);
|
||||
bool xfs_buf_log_check_iovec(struct xfs_log_iovec *iovec);
|
||||
|
||||
unsigned int xfs_buf_inval_log_space(unsigned int map_count,
|
||||
unsigned int blocksize);
|
||||
|
||||
extern struct kmem_cache *xfs_buf_item_cache;
|
||||
|
||||
#endif /* __XFS_BUF_ITEM_H__ */
|
||||
|
||||
+16
-1
@@ -167,6 +167,14 @@ xfs_discard_extents(
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Care must be taken setting up the trim cursor as the perags may not have been
|
||||
* initialised when the cursor is initialised. e.g. a clean mount which hasn't
|
||||
* read in AGFs and the first operation run on the mounted fs is a trim. This
|
||||
* can result in perag fields that aren't initialised until
|
||||
* xfs_trim_gather_extents() calls xfs_alloc_read_agf() to lock down the AG for
|
||||
* the free space search.
|
||||
*/
|
||||
struct xfs_trim_cur {
|
||||
xfs_agblock_t start;
|
||||
xfs_extlen_t count;
|
||||
@@ -204,6 +212,14 @@ xfs_trim_gather_extents(
|
||||
if (error)
|
||||
goto out_trans_cancel;
|
||||
|
||||
/*
|
||||
* First time through tcur->count will not have been initialised as
|
||||
* pag->pagf_longest is not guaranteed to be valid before we read
|
||||
* the AGF buffer above.
|
||||
*/
|
||||
if (!tcur->count)
|
||||
tcur->count = pag->pagf_longest;
|
||||
|
||||
if (tcur->by_bno) {
|
||||
/* sub-AG discard request always starts at tcur->start */
|
||||
cur = xfs_bnobt_init_cursor(mp, tp, agbp, pag);
|
||||
@@ -350,7 +366,6 @@ xfs_trim_perag_extents(
|
||||
{
|
||||
struct xfs_trim_cur tcur = {
|
||||
.start = start,
|
||||
.count = pag->pagf_longest,
|
||||
.end = end,
|
||||
.minlen = minlen,
|
||||
};
|
||||
|
||||
@@ -83,6 +83,11 @@ xfs_efi_item_size(
|
||||
*nbytes += xfs_efi_log_format_sizeof(efip->efi_format.efi_nextents);
|
||||
}
|
||||
|
||||
unsigned int xfs_efi_log_space(unsigned int nr)
|
||||
{
|
||||
return xlog_item_space(1, xfs_efi_log_format_sizeof(nr));
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called to fill in the vector of log iovecs for the
|
||||
* given efi log item. We use only 1 iovec, and we point that
|
||||
@@ -254,6 +259,11 @@ xfs_efd_item_size(
|
||||
*nbytes += xfs_efd_log_format_sizeof(efdp->efd_format.efd_nextents);
|
||||
}
|
||||
|
||||
unsigned int xfs_efd_log_space(unsigned int nr)
|
||||
{
|
||||
return xlog_item_space(1, xfs_efd_log_format_sizeof(nr));
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called to fill in the vector of log iovecs for the
|
||||
* given efd log item. We use only 1 iovec, and we point that
|
||||
|
||||
@@ -94,4 +94,7 @@ void xfs_extent_free_defer_add(struct xfs_trans *tp,
|
||||
struct xfs_extent_free_item *xefi,
|
||||
struct xfs_defer_pending **dfpp);
|
||||
|
||||
unsigned int xfs_efi_log_space(unsigned int nr);
|
||||
unsigned int xfs_efd_log_space(unsigned int nr);
|
||||
|
||||
#endif /* __XFS_EXTFREE_ITEM_H__ */
|
||||
|
||||
+78
-9
@@ -576,7 +576,10 @@ xfs_dio_write_end_io(
|
||||
nofs_flag = memalloc_nofs_save();
|
||||
|
||||
if (flags & IOMAP_DIO_COW) {
|
||||
error = xfs_reflink_end_cow(ip, offset, size);
|
||||
if (iocb->ki_flags & IOCB_ATOMIC)
|
||||
error = xfs_reflink_end_atomic_cow(ip, offset, size);
|
||||
else
|
||||
error = xfs_reflink_end_cow(ip, offset, size);
|
||||
if (error)
|
||||
goto out;
|
||||
}
|
||||
@@ -725,6 +728,72 @@ xfs_file_dio_write_zoned(
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle block atomic writes
|
||||
*
|
||||
* Two methods of atomic writes are supported:
|
||||
* - REQ_ATOMIC-based, which would typically use some form of HW offload in the
|
||||
* disk
|
||||
* - COW-based, which uses a COW fork as a staging extent for data updates
|
||||
* before atomically updating extent mappings for the range being written
|
||||
*
|
||||
*/
|
||||
static noinline ssize_t
|
||||
xfs_file_dio_write_atomic(
|
||||
struct xfs_inode *ip,
|
||||
struct kiocb *iocb,
|
||||
struct iov_iter *from)
|
||||
{
|
||||
unsigned int iolock = XFS_IOLOCK_SHARED;
|
||||
ssize_t ret, ocount = iov_iter_count(from);
|
||||
const struct iomap_ops *dops;
|
||||
|
||||
/*
|
||||
* HW offload should be faster, so try that first if it is already
|
||||
* known that the write length is not too large.
|
||||
*/
|
||||
if (ocount > xfs_inode_buftarg(ip)->bt_bdev_awu_max)
|
||||
dops = &xfs_atomic_write_cow_iomap_ops;
|
||||
else
|
||||
dops = &xfs_direct_write_iomap_ops;
|
||||
|
||||
retry:
|
||||
ret = xfs_ilock_iocb_for_write(iocb, &iolock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = xfs_file_write_checks(iocb, from, &iolock, NULL);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
/* Demote similar to xfs_file_dio_write_aligned() */
|
||||
if (iolock == XFS_IOLOCK_EXCL) {
|
||||
xfs_ilock_demote(ip, XFS_IOLOCK_EXCL);
|
||||
iolock = XFS_IOLOCK_SHARED;
|
||||
}
|
||||
|
||||
trace_xfs_file_direct_write(iocb, from);
|
||||
ret = iomap_dio_rw(iocb, from, dops, &xfs_dio_write_ops,
|
||||
0, NULL, 0);
|
||||
|
||||
/*
|
||||
* The retry mechanism is based on the ->iomap_begin method returning
|
||||
* -ENOPROTOOPT, which would be when the REQ_ATOMIC-based write is not
|
||||
* possible. The REQ_ATOMIC-based method typically not be possible if
|
||||
* the write spans multiple extents or the disk blocks are misaligned.
|
||||
*/
|
||||
if (ret == -ENOPROTOOPT && dops == &xfs_direct_write_iomap_ops) {
|
||||
xfs_iunlock(ip, iolock);
|
||||
dops = &xfs_atomic_write_cow_iomap_ops;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
if (iolock)
|
||||
xfs_iunlock(ip, iolock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle block unaligned direct I/O writes
|
||||
*
|
||||
@@ -840,6 +909,8 @@ xfs_file_dio_write(
|
||||
return xfs_file_dio_write_unaligned(ip, iocb, from);
|
||||
if (xfs_is_zoned_inode(ip))
|
||||
return xfs_file_dio_write_zoned(ip, iocb, from);
|
||||
if (iocb->ki_flags & IOCB_ATOMIC)
|
||||
return xfs_file_dio_write_atomic(ip, iocb, from);
|
||||
return xfs_file_dio_write_aligned(ip, iocb, from,
|
||||
&xfs_direct_write_iomap_ops, &xfs_dio_write_ops, NULL);
|
||||
}
|
||||
@@ -1032,14 +1103,12 @@ xfs_file_write_iter(
|
||||
return xfs_file_dax_write(iocb, from);
|
||||
|
||||
if (iocb->ki_flags & IOCB_ATOMIC) {
|
||||
/*
|
||||
* Currently only atomic writing of a single FS block is
|
||||
* supported. It would be possible to atomic write smaller than
|
||||
* a FS block, but there is no requirement to support this.
|
||||
* Note that iomap also does not support this yet.
|
||||
*/
|
||||
if (ocount != ip->i_mount->m_sb.sb_blocksize)
|
||||
if (ocount < xfs_get_atomic_write_min(ip))
|
||||
return -EINVAL;
|
||||
|
||||
if (ocount > xfs_get_atomic_write_max(ip))
|
||||
return -EINVAL;
|
||||
|
||||
ret = generic_atomic_write_valid(iocb, from);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -1488,7 +1557,7 @@ xfs_file_open(
|
||||
if (xfs_is_shutdown(XFS_M(inode->i_sb)))
|
||||
return -EIO;
|
||||
file->f_mode |= FMODE_NOWAIT | FMODE_CAN_ODIRECT;
|
||||
if (xfs_inode_can_atomicwrite(XFS_I(inode)))
|
||||
if (xfs_get_atomic_write_min(XFS_I(inode)) > 0)
|
||||
file->f_mode |= FMODE_CAN_ATOMIC_WRITE;
|
||||
return generic_file_open(inode, file);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user