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:
Linus Torvalds
2025-05-26 12:56:01 -07:00
48 changed files with 1521 additions and 188 deletions
+11
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+5 -1
View File
@@ -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;
}
+5
View File
@@ -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,
+5 -1
View File
@@ -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)
+4
View File
@@ -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
View File
@@ -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;
}
+25
View File
@@ -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__ */
-2
View File
@@ -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;
+10
View File
@@ -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
+3
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+19
View File
@@ -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.
+3
View File
@@ -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
View File
@@ -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,
};
+10
View File
@@ -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
+3
View File
@@ -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
View File
@@ -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