mirror of
https://github.com/torvalds/linux.git
synced 2026-09-11 20:13:02 +02:00
xfs: fixes for 7.3-rc2
Signed-off-by: Carlos Maiolino <cem@kernel.org> -----BEGIN PGP SIGNATURE----- iJUEABMJAB0WIQSmtYVZ/MfVMGUq1GNcsMJ8RxYuYwUCapUNuQAKCRBcsMJ8RxYu Y/QVAX9SDXNSP3dw04wAuYgwSH5Ftm+WAnwusAsSvJkQdTvU0nEpAHyjb6WokS5a EbOGy5UBfRyqJFOmOw6wF5Ax0Aoxrt+lN8CuoDoh6aEhtYlh0jvd50ustYX8QSas W2R9B6IFIw== =JWP4 -----END PGP SIGNATURE----- Merge tag 'xfs-fixes-7.3-rc2' of gitolite.kernel.org:/pub/scm/fs/xfs/xfs-linux Pull xfs fixes from Carlos Maiolino: "This contains a few fixes for the zoned storage support, a possible deadlock vector fix, some code refactoring patches and a quota evasion fix on XFS while exporting it via NFS. Please note that for the quota evasion fix, a couple patches for the capability subsystem are included in the pull request. Those have been ack'ed by the respective maintainer which also agreed to have them going through the xfs tree. This also includes a patch for the quota subsystem to stop issuing audit messages during quota enforcing. Quota maintainer also ack'ed and agreed with this going through xfs tree" * tag 'xfs-fixes-7.3-rc2' of gitolite.kernel.org:/pub/scm/fs/xfs/xfs-linux: capability: unexport has_capability_noaudit xfs: replace ns_capable_noaudit quota: Don't issue audit messages on quota enforcing capability: Add new capable_noaudit xfs: fix capability check in xfs xfs: restore bi_bdev in xfs_zone_gc_write_chunk xfs: split ioend handling into a separate source file xfs: factor out a xfs_iomap_set_anon_write helper xfs: fix zoned write iomap flags assignments xfs: fix racy open zone caching xfs: handle NULL open_zone for merged ioends in xfs_ioend_put_open_zones xfs: use inode_init_always_gfp with __GFP_NOFAIL in xfs_inode_alloc xfs: remove kmem_to_page() xfs: don't flush and invalidate internal RT device twice in xfs_shutdown_devices xfs: split an assert in xfs_trans_log_buf xfs: don't hold buffer locks across sync transaction commit in xfs_sync_sb_buf
This commit is contained in:
commit
a7f25dc23f
|
|
@ -1240,7 +1240,7 @@ static int ignore_hardlimit(struct dquot *dquot)
|
|||
{
|
||||
struct mem_dqinfo *info = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_id.type];
|
||||
|
||||
return capable(CAP_SYS_RESOURCE) &&
|
||||
return capable_noaudit(CAP_SYS_RESOURCE) &&
|
||||
(info->dqi_format->qf_fmt_id != QFMT_VFS_OLD ||
|
||||
!(info->dqi_flags & DQF_ROOT_SQUASH));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ xfs-y += xfs_aops.o \
|
|||
xfs_healthmon.o \
|
||||
xfs_icache.o \
|
||||
xfs_ioctl.o \
|
||||
xfs_ioend.o \
|
||||
xfs_iomap.o \
|
||||
xfs_iops.o \
|
||||
xfs_inode.o \
|
||||
|
|
|
|||
|
|
@ -359,7 +359,11 @@ static inline int xfs_initialize_rtgroups(struct xfs_mount *mp,
|
|||
# define xfs_rtgroup_unlock(rtg, gf) ((void)0)
|
||||
# define xfs_rtgroup_trans_join(tp, rtg, gf) ((void)0)
|
||||
# define xfs_update_rtsb(bp, sb_bp) ((void)0)
|
||||
# define xfs_log_rtsb(tp, sb_bp) (NULL)
|
||||
static inline struct xfs_buf *xfs_log_rtsb(struct xfs_trans *tp,
|
||||
const struct xfs_buf *sb_bp)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
# define xfs_rtgroup_get_geometry(rtg, rgeo) (-EOPNOTSUPP)
|
||||
#endif /* CONFIG_XFS_RT */
|
||||
|
||||
|
|
|
|||
|
|
@ -1470,36 +1470,33 @@ xfs_sync_sb_buf(
|
|||
bool update_rtsb)
|
||||
{
|
||||
struct xfs_trans *tp;
|
||||
struct xfs_buf *bp;
|
||||
struct xfs_buf *rtsb_bp = NULL;
|
||||
int error;
|
||||
|
||||
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_sb, 0, 0, 0, &tp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
bp = xfs_trans_getsb(tp);
|
||||
xfs_log_sb(tp);
|
||||
xfs_trans_bhold(tp, bp);
|
||||
if (update_rtsb) {
|
||||
rtsb_bp = xfs_log_rtsb(tp, bp);
|
||||
if (rtsb_bp)
|
||||
xfs_trans_bhold(tp, rtsb_bp);
|
||||
}
|
||||
if (update_rtsb)
|
||||
xfs_log_rtsb(tp, xfs_trans_getsb(tp));
|
||||
xfs_trans_set_sync(tp);
|
||||
error = xfs_trans_commit(tp);
|
||||
if (error)
|
||||
goto out;
|
||||
/*
|
||||
* write out the sb buffer to get the changes to disk
|
||||
*/
|
||||
error = xfs_bwrite(bp);
|
||||
if (!error && rtsb_bp)
|
||||
error = xfs_bwrite(rtsb_bp);
|
||||
out:
|
||||
if (rtsb_bp)
|
||||
xfs_buf_relse(rtsb_bp);
|
||||
xfs_buf_relse(bp);
|
||||
return error;
|
||||
|
||||
/* Re-acquire and write the sb and rtsb to disk. */
|
||||
xfs_buf_lock(mp->m_sb_bp);
|
||||
error = xfs_bwrite(mp->m_sb_bp);
|
||||
xfs_buf_unlock(mp->m_sb_bp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (update_rtsb && mp->m_rtsb_bp) {
|
||||
xfs_buf_lock(mp->m_rtsb_bp);
|
||||
error = xfs_bwrite(mp->m_rtsb_bp);
|
||||
xfs_buf_unlock(mp->m_rtsb_bp);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "xfs_errortag.h"
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_icache.h"
|
||||
#include "xfs_ioend.h"
|
||||
#include "xfs_zone_alloc.h"
|
||||
#include "xfs_rtgroup.h"
|
||||
#include <linux/bio-integrity.h>
|
||||
|
|
@ -36,15 +37,6 @@ XFS_WPC(struct iomap_writepage_ctx *ctx)
|
|||
return container_of(ctx, struct xfs_writepage_ctx, ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fast and loose check if this write could update the on-disk inode size.
|
||||
*/
|
||||
static inline bool xfs_ioend_is_append(struct iomap_ioend *ioend)
|
||||
{
|
||||
return ioend->io_offset + ioend->io_size >
|
||||
XFS_I(ioend->io_inode)->i_disk_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update on-disk file size now that data has been written to disk.
|
||||
*/
|
||||
|
|
@ -80,175 +72,6 @@ xfs_setfilesize(
|
|||
return xfs_trans_commit(tp);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_ioend_put_open_zones(
|
||||
struct iomap_ioend *ioend)
|
||||
{
|
||||
struct iomap_ioend *tmp;
|
||||
|
||||
/*
|
||||
* Put the open zone for all ioends merged into this one (if any).
|
||||
*/
|
||||
list_for_each_entry(tmp, &ioend->io_list, io_list)
|
||||
xfs_open_zone_put(tmp->io_private);
|
||||
|
||||
/*
|
||||
* The main ioend might not have an open zone if the submission failed
|
||||
* before xfs_zone_alloc_and_submit got called.
|
||||
*/
|
||||
if (ioend->io_private)
|
||||
xfs_open_zone_put(ioend->io_private);
|
||||
}
|
||||
|
||||
/*
|
||||
* IO write completion.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_end_ioend_write(
|
||||
struct iomap_ioend *ioend)
|
||||
{
|
||||
struct xfs_inode *ip = XFS_I(ioend->io_inode);
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
bool is_zoned = xfs_is_zoned_inode(ip);
|
||||
xfs_off_t offset = ioend->io_offset;
|
||||
size_t size = ioend->io_size;
|
||||
unsigned int nofs_flag;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* We can allocate memory here while doing writeback on behalf of
|
||||
* memory reclaim. To avoid memory allocation deadlocks set the
|
||||
* task-wide nofs context for the following operations.
|
||||
*/
|
||||
nofs_flag = memalloc_nofs_save();
|
||||
|
||||
/*
|
||||
* Just clean up the in-memory structures if the fs has been shut down.
|
||||
*/
|
||||
if (xfs_is_shutdown(mp)) {
|
||||
error = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up all COW blocks and underlying data fork delalloc blocks on
|
||||
* I/O error. The delalloc punch is required because this ioend was
|
||||
* mapped to blocks in the COW fork and the associated pages are no
|
||||
* longer dirty. If we don't remove delalloc blocks here, they become
|
||||
* stale and can corrupt free space accounting on unmount.
|
||||
*/
|
||||
error = blk_status_to_errno(ioend->io_bio.bi_status);
|
||||
if (unlikely(error)) {
|
||||
/*
|
||||
* Zoned writes update the in-core open zone accounting before
|
||||
* I/O submission. A failed write leaves that state
|
||||
* inconsistent, so shut down the filesystem instead of letting
|
||||
* later writers wait forever for open zone space to become
|
||||
* available.
|
||||
*/
|
||||
if (is_zoned) {
|
||||
xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
|
||||
goto done;
|
||||
}
|
||||
if (ioend->io_flags & IOMAP_IOEND_SHARED) {
|
||||
ASSERT(!is_zoned);
|
||||
xfs_reflink_cancel_cow_range(ip, offset, size, true);
|
||||
xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK, offset,
|
||||
offset + size, NULL);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Success: commit the COW or unwritten blocks if needed.
|
||||
*/
|
||||
if (is_zoned)
|
||||
error = xfs_zoned_end_io(ip, offset, size, ioend->io_sector,
|
||||
ioend->io_private, NULLFSBLOCK);
|
||||
else if (ioend->io_flags & IOMAP_IOEND_SHARED)
|
||||
error = xfs_reflink_end_cow(ip, offset, size);
|
||||
else if (ioend->io_flags & IOMAP_IOEND_UNWRITTEN)
|
||||
error = xfs_iomap_write_unwritten(ip, offset, size, false);
|
||||
|
||||
if (!error &&
|
||||
!(ioend->io_flags & IOMAP_IOEND_DIRECT) &&
|
||||
xfs_ioend_is_append(ioend))
|
||||
error = xfs_setfilesize(ip, offset, size);
|
||||
done:
|
||||
if (is_zoned)
|
||||
xfs_ioend_put_open_zones(ioend);
|
||||
iomap_finish_ioends(ioend, error);
|
||||
memalloc_nofs_restore(nofs_flag);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finish all pending IO completions that require transactional modifications.
|
||||
*
|
||||
* We try to merge physical and logically contiguous ioends before completion to
|
||||
* minimise the number of transactions we need to perform during IO completion.
|
||||
* Both unwritten extent conversion and COW remapping need to iterate and modify
|
||||
* one physical extent at a time, so we gain nothing by merging physically
|
||||
* discontiguous extents here.
|
||||
*
|
||||
* The ioend chain length that we can be processing here is largely unbound in
|
||||
* length and we may have to perform significant amounts of work on each ioend
|
||||
* to complete it. Hence we have to be careful about holding the CPU for too
|
||||
* long in this loop.
|
||||
*/
|
||||
void
|
||||
xfs_end_io(
|
||||
struct work_struct *work)
|
||||
{
|
||||
struct xfs_inode *ip =
|
||||
container_of(work, struct xfs_inode, i_ioend_work);
|
||||
struct iomap_ioend *ioend;
|
||||
struct list_head tmp;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ip->i_ioend_lock, flags);
|
||||
list_replace_init(&ip->i_ioend_list, &tmp);
|
||||
spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
|
||||
|
||||
iomap_sort_ioends(&tmp);
|
||||
while ((ioend = list_first_entry_or_null(&tmp, struct iomap_ioend,
|
||||
io_list))) {
|
||||
list_del_init(&ioend->io_list);
|
||||
iomap_ioend_try_merge(ioend, &tmp);
|
||||
if (bio_op(&ioend->io_bio) == REQ_OP_READ)
|
||||
iomap_finish_ioends(ioend,
|
||||
blk_status_to_errno(ioend->io_bio.bi_status));
|
||||
else
|
||||
xfs_end_ioend_write(ioend);
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
xfs_end_bio(
|
||||
struct bio *bio)
|
||||
{
|
||||
struct iomap_ioend *ioend = iomap_ioend_from_bio(bio);
|
||||
struct xfs_inode *ip = XFS_I(ioend->io_inode);
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* For Appends record the actually written block number and set the
|
||||
* boundary flag if needed.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_XFS_RT) && bio_is_zone_append(bio)) {
|
||||
ioend->io_sector = bio->bi_iter.bi_sector;
|
||||
xfs_mark_rtg_boundary(ioend);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ip->i_ioend_lock, flags);
|
||||
if (list_empty(&ip->i_ioend_list))
|
||||
WARN_ON_ONCE(!queue_work(mp->m_unwritten_workqueue,
|
||||
&ip->i_ioend_work));
|
||||
list_add_tail(&ioend->io_list, &ip->i_ioend_list);
|
||||
spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* We cannot cancel the ioend directly on error. We may have already set other
|
||||
* pages under writeback and hence we have to run I/O completion to mark the
|
||||
|
|
@ -631,13 +454,8 @@ xfs_zoned_map_blocks(
|
|||
XFS_BMAPI_REMAP);
|
||||
xfs_iunlock(ip, XFS_ILOCK_EXCL);
|
||||
|
||||
wpc->iomap.type = IOMAP_MAPPED;
|
||||
wpc->iomap.flags = IOMAP_F_DIRTY;
|
||||
wpc->iomap.bdev = mp->m_rtdev_targp->bt_bdev;
|
||||
wpc->iomap.offset = offset;
|
||||
wpc->iomap.length = XFS_FSB_TO_B(mp, count_fsb);
|
||||
wpc->iomap.flags = IOMAP_F_ANON_WRITE;
|
||||
|
||||
xfs_iomap_set_anon_write(ip, &wpc->iomap, offset,
|
||||
XFS_FSB_TO_B(mp, count_fsb));
|
||||
trace_xfs_zoned_map_blocks(ip, offset, wpc->iomap.length);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,5 @@ extern const struct address_space_operations xfs_address_space_operations;
|
|||
extern const struct address_space_operations xfs_dax_aops;
|
||||
|
||||
int xfs_setfilesize(struct xfs_inode *ip, xfs_off_t offset, size_t size);
|
||||
void xfs_end_bio(struct bio *bio);
|
||||
|
||||
#endif /* __XFS_AOPS_H__ */
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
#include "xfs_iomap.h"
|
||||
#include "xfs_reflink.h"
|
||||
#include "xfs_file.h"
|
||||
#include "xfs_aops.h"
|
||||
#include "xfs_ioend.h"
|
||||
#include "xfs_zone_alloc.h"
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_errortag.h"
|
||||
|
|
|
|||
|
|
@ -1174,8 +1174,7 @@ xfs_getfsmap(
|
|||
if (!xfs_getfsmap_check_keys(&head->fmh_keys[0], &head->fmh_keys[1]))
|
||||
return -EINVAL;
|
||||
|
||||
use_rmap = xfs_has_rmapbt(mp) &&
|
||||
has_capability_noaudit(current, CAP_SYS_ADMIN);
|
||||
use_rmap = xfs_has_rmapbt(mp) && capable_noaudit(CAP_SYS_ADMIN);
|
||||
head->fmh_entries = 0;
|
||||
|
||||
/* Set up our device handlers. */
|
||||
|
|
|
|||
|
|
@ -82,24 +82,20 @@ static inline xa_mark_t ici_tag_to_mark(unsigned int tag)
|
|||
|
||||
/*
|
||||
* Allocate and initialise an xfs_inode.
|
||||
*
|
||||
* This can happen in context of already dirtied transactions, so the memory
|
||||
* allocations must not fail.
|
||||
*/
|
||||
struct xfs_inode *
|
||||
xfs_inode_alloc(
|
||||
struct xfs_mount *mp,
|
||||
xfs_ino_t ino)
|
||||
{
|
||||
gfp_t gfp = GFP_KERNEL | __GFP_NOFAIL;
|
||||
struct xfs_inode *ip;
|
||||
|
||||
/*
|
||||
* XXX: If this didn't occur in transactions, we could drop GFP_NOFAIL
|
||||
* and return NULL here on ENOMEM.
|
||||
*/
|
||||
ip = alloc_inode_sb(mp->m_super, xfs_inode_cache, GFP_KERNEL | __GFP_NOFAIL);
|
||||
|
||||
if (inode_init_always(mp->m_super, VFS_I(ip))) {
|
||||
kmem_cache_free(xfs_inode_cache, ip);
|
||||
return NULL;
|
||||
}
|
||||
ip = alloc_inode_sb(mp->m_super, xfs_inode_cache, gfp);
|
||||
inode_init_always_gfp(mp->m_super, VFS_I(ip), gfp);
|
||||
|
||||
VFS_I(ip)->i_ino = ino;
|
||||
/* VFS doesn't initialise i_mode! */
|
||||
|
|
|
|||
|
|
@ -647,7 +647,7 @@ xfs_ioctl_setattr_get_trans(
|
|||
goto out_error;
|
||||
|
||||
error = xfs_trans_alloc_ichange(ip, NULL, NULL, pdqp,
|
||||
has_capability_noaudit(current, CAP_FOWNER), &tp);
|
||||
capable_noaudit(CAP_FOWNER), &tp);
|
||||
if (error)
|
||||
goto out_error;
|
||||
|
||||
|
|
|
|||
184
fs/xfs/xfs_ioend.c
Normal file
184
fs/xfs/xfs_ioend.c
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2016-2025 Christoph Hellwig.
|
||||
* All Rights Reserved.
|
||||
*/
|
||||
#include "xfs_platform.h"
|
||||
#include "xfs_shared.h"
|
||||
#include "xfs_format.h"
|
||||
#include "xfs_log_format.h"
|
||||
#include "xfs_trans_resv.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_iomap.h"
|
||||
#include "xfs_trace.h"
|
||||
#include "xfs_bmap_util.h"
|
||||
#include "xfs_reflink.h"
|
||||
#include "xfs_zone_alloc.h"
|
||||
#include "xfs_ioend.h"
|
||||
|
||||
static void
|
||||
xfs_ioend_put_open_zones(
|
||||
struct iomap_ioend *ioend)
|
||||
{
|
||||
struct iomap_ioend *tmp;
|
||||
|
||||
/*
|
||||
* Put the open zone for all ioends merged into this one (if any).
|
||||
*/
|
||||
list_for_each_entry(tmp, &ioend->io_list, io_list)
|
||||
xfs_open_zone_put(tmp->io_private);
|
||||
|
||||
/*
|
||||
* The main ioend might not have an open zone if the submission failed
|
||||
* before xfs_zone_alloc_and_submit got called.
|
||||
*/
|
||||
if (ioend->io_private)
|
||||
xfs_open_zone_put(ioend->io_private);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_end_ioend_write(
|
||||
struct iomap_ioend *ioend)
|
||||
{
|
||||
struct xfs_inode *ip = XFS_I(ioend->io_inode);
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
bool is_zoned = xfs_is_zoned_inode(ip);
|
||||
xfs_off_t offset = ioend->io_offset;
|
||||
size_t size = ioend->io_size;
|
||||
unsigned int nofs_flag;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* We can allocate memory here while doing writeback on behalf of
|
||||
* memory reclaim. To avoid memory allocation deadlocks set the
|
||||
* task-wide nofs context for the following operations.
|
||||
*/
|
||||
nofs_flag = memalloc_nofs_save();
|
||||
|
||||
/*
|
||||
* Just clean up the in-memory structures if the fs has been shut down.
|
||||
*/
|
||||
if (xfs_is_shutdown(mp)) {
|
||||
error = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up all COW blocks and underlying data fork delalloc blocks on
|
||||
* I/O error. The delalloc punch is required because this ioend was
|
||||
* mapped to blocks in the COW fork and the associated pages are no
|
||||
* longer dirty. If we don't remove delalloc blocks here, they become
|
||||
* stale and can corrupt free space accounting on unmount.
|
||||
*/
|
||||
error = blk_status_to_errno(ioend->io_bio.bi_status);
|
||||
if (unlikely(error)) {
|
||||
/*
|
||||
* Zoned writes update the in-core open zone accounting before
|
||||
* I/O submission. A failed write leaves that state
|
||||
* inconsistent, so shut down the filesystem instead of letting
|
||||
* later writers wait forever for open zone space to become
|
||||
* available.
|
||||
*/
|
||||
if (is_zoned) {
|
||||
xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
|
||||
goto done;
|
||||
}
|
||||
if (ioend->io_flags & IOMAP_IOEND_SHARED) {
|
||||
ASSERT(!is_zoned);
|
||||
xfs_reflink_cancel_cow_range(ip, offset, size, true);
|
||||
xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK, offset,
|
||||
offset + size, NULL);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Success: commit the COW or unwritten blocks if needed.
|
||||
*/
|
||||
if (is_zoned)
|
||||
error = xfs_zoned_end_io(ip, offset, size, ioend->io_sector,
|
||||
ioend->io_private, NULLFSBLOCK);
|
||||
else if (ioend->io_flags & IOMAP_IOEND_SHARED)
|
||||
error = xfs_reflink_end_cow(ip, offset, size);
|
||||
else if (ioend->io_flags & IOMAP_IOEND_UNWRITTEN)
|
||||
error = xfs_iomap_write_unwritten(ip, offset, size, false);
|
||||
|
||||
if (!error &&
|
||||
!(ioend->io_flags & IOMAP_IOEND_DIRECT) &&
|
||||
xfs_ioend_is_append(ioend))
|
||||
error = xfs_setfilesize(ip, offset, size);
|
||||
done:
|
||||
if (is_zoned)
|
||||
xfs_ioend_put_open_zones(ioend);
|
||||
iomap_finish_ioends(ioend, error);
|
||||
memalloc_nofs_restore(nofs_flag);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finish all pending IO completions that require transactional modifications.
|
||||
*
|
||||
* We try to merge physical and logically contiguous ioends before completion to
|
||||
* minimise the number of transactions we need to perform during IO completion.
|
||||
* Both unwritten extent conversion and COW remapping need to iterate and modify
|
||||
* one physical extent at a time, so we gain nothing by merging physically
|
||||
* discontiguous extents here.
|
||||
*
|
||||
* The ioend chain length that we can be processing here is largely unbound in
|
||||
* length and we may have to perform significant amounts of work on each ioend
|
||||
* to complete it. Hence we have to be careful about holding the CPU for too
|
||||
* long in this loop.
|
||||
*/
|
||||
void
|
||||
xfs_end_io(
|
||||
struct work_struct *work)
|
||||
{
|
||||
struct xfs_inode *ip =
|
||||
container_of(work, struct xfs_inode, i_ioend_work);
|
||||
struct iomap_ioend *ioend;
|
||||
struct list_head tmp;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ip->i_ioend_lock, flags);
|
||||
list_replace_init(&ip->i_ioend_list, &tmp);
|
||||
spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
|
||||
|
||||
iomap_sort_ioends(&tmp);
|
||||
while ((ioend = list_first_entry_or_null(&tmp, struct iomap_ioend,
|
||||
io_list))) {
|
||||
list_del_init(&ioend->io_list);
|
||||
iomap_ioend_try_merge(ioend, &tmp);
|
||||
if (bio_op(&ioend->io_bio) == REQ_OP_READ)
|
||||
iomap_finish_ioends(ioend,
|
||||
blk_status_to_errno(ioend->io_bio.bi_status));
|
||||
else
|
||||
xfs_end_ioend_write(ioend);
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
xfs_end_bio(
|
||||
struct bio *bio)
|
||||
{
|
||||
struct iomap_ioend *ioend = iomap_ioend_from_bio(bio);
|
||||
struct xfs_inode *ip = XFS_I(ioend->io_inode);
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* For Appends record the actually written block number and set the
|
||||
* boundary flag if needed.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_XFS_RT) && bio_is_zone_append(bio)) {
|
||||
ioend->io_sector = bio->bi_iter.bi_sector;
|
||||
xfs_mark_rtg_boundary(ioend);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ip->i_ioend_lock, flags);
|
||||
if (list_empty(&ip->i_ioend_list))
|
||||
WARN_ON_ONCE(!queue_work(mp->m_unwritten_workqueue,
|
||||
&ip->i_ioend_work));
|
||||
list_add_tail(&ioend->io_list, &ip->i_ioend_list);
|
||||
spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
|
||||
}
|
||||
16
fs/xfs/xfs_ioend.h
Normal file
16
fs/xfs/xfs_ioend.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __XFS_IOEND_H
|
||||
#define __XFS_IOEND_H
|
||||
|
||||
/*
|
||||
* Fast and loose check if this write could update the on-disk inode size.
|
||||
*/
|
||||
static inline bool xfs_ioend_is_append(struct iomap_ioend *ioend)
|
||||
{
|
||||
return ioend->io_offset + ioend->io_size >
|
||||
XFS_I(ioend->io_inode)->i_disk_size;
|
||||
}
|
||||
|
||||
void xfs_end_bio(struct bio *bio);
|
||||
|
||||
#endif /* __XFS_IOEND_H */
|
||||
|
|
@ -1083,12 +1083,7 @@ xfs_zoned_direct_write_iomap_begin(
|
|||
return error;
|
||||
}
|
||||
|
||||
iomap->type = IOMAP_MAPPED;
|
||||
iomap->flags = IOMAP_F_DIRTY;
|
||||
iomap->bdev = ip->i_mount->m_rtdev_targp->bt_bdev;
|
||||
iomap->offset = offset;
|
||||
iomap->length = length;
|
||||
iomap->flags = IOMAP_F_ANON_WRITE;
|
||||
xfs_iomap_set_anon_write(ip, iomap, offset, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,20 @@ int xfs_zero_range(struct xfs_inode *ip, loff_t pos, loff_t len,
|
|||
int xfs_truncate_page(struct xfs_inode *ip, loff_t pos,
|
||||
struct xfs_zone_alloc_ctx *ac, bool *did_zero);
|
||||
|
||||
static inline void
|
||||
xfs_iomap_set_anon_write(
|
||||
struct xfs_inode *ip,
|
||||
struct iomap *iomap,
|
||||
loff_t offset,
|
||||
loff_t length)
|
||||
{
|
||||
iomap->type = IOMAP_MAPPED;
|
||||
iomap->bdev = ip->i_mount->m_rtdev_targp->bt_bdev;
|
||||
iomap->offset = offset;
|
||||
iomap->length = length;
|
||||
iomap->flags = IOMAP_F_ANON_WRITE | IOMAP_F_DIRTY;
|
||||
}
|
||||
|
||||
static inline xfs_filblks_t
|
||||
xfs_aligned_fsb_count(
|
||||
xfs_fileoff_t offset_fsb,
|
||||
|
|
|
|||
|
|
@ -834,7 +834,7 @@ xfs_setattr_nonsize(
|
|||
}
|
||||
|
||||
error = xfs_trans_alloc_ichange(ip, udqp, gdqp, NULL,
|
||||
has_capability_noaudit(current, CAP_FOWNER), &tp);
|
||||
capable_noaudit(CAP_FOWNER), &tp);
|
||||
if (error)
|
||||
goto out_dqrele;
|
||||
|
||||
|
|
|
|||
|
|
@ -289,15 +289,4 @@ int xfs_rw_bdev(struct block_device *bdev, sector_t sector, unsigned int count,
|
|||
# define PTR_FMT "%p"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper for IO routines to grab backing pages from allocated kernel memory.
|
||||
*/
|
||||
static inline struct page *
|
||||
kmem_to_page(void *addr)
|
||||
{
|
||||
if (is_vmalloc_addr(addr))
|
||||
return vmalloc_to_page(addr);
|
||||
return virt_to_page(addr);
|
||||
}
|
||||
|
||||
#endif /* _XFS_PLATFORM_H */
|
||||
|
|
|
|||
|
|
@ -445,7 +445,7 @@ xfs_shutdown_devices(
|
|||
blkdev_issue_flush(mp->m_logdev_targp->bt_bdev);
|
||||
invalidate_bdev(mp->m_logdev_targp->bt_bdev);
|
||||
}
|
||||
if (mp->m_rtdev_targp) {
|
||||
if (mp->m_rtdev_targp && mp->m_rtdev_targp != mp->m_ddev_targp) {
|
||||
blkdev_issue_flush(mp->m_rtdev_targp->bt_bdev);
|
||||
invalidate_bdev(mp->m_rtdev_targp->bt_bdev);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -521,7 +521,8 @@ xfs_trans_log_buf(
|
|||
{
|
||||
struct xfs_buf_log_item *bip = bp->b_log_item;
|
||||
|
||||
ASSERT(first <= last && last < BBTOB(bp->b_length));
|
||||
ASSERT(first <= last);
|
||||
ASSERT(last < BBTOB(bp->b_length));
|
||||
ASSERT(!(bip->bli_flags & XFS_BLI_ORDERED));
|
||||
|
||||
xfs_trans_dirty_buf(tp, bp);
|
||||
|
|
|
|||
|
|
@ -793,17 +793,35 @@ xfs_get_cached_zone(
|
|||
|
||||
rcu_read_lock();
|
||||
oz = VFS_I(ip)->i_private;
|
||||
if (oz) {
|
||||
/*
|
||||
* GC only steals open zones at mount time, so no GC zones
|
||||
* should end up in the cache.
|
||||
*/
|
||||
ASSERT(!oz->oz_is_gc);
|
||||
if (!atomic_inc_not_zero(&oz->oz_ref))
|
||||
oz = NULL;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
if (!oz)
|
||||
goto out_unlock;
|
||||
|
||||
/*
|
||||
* GC only steals open zones at mount time, so no GC zones should end up
|
||||
* in the cache.
|
||||
*/
|
||||
ASSERT(!oz->oz_is_gc);
|
||||
|
||||
/*
|
||||
* Drop the old cached open zone if it is full.
|
||||
*/
|
||||
if (oz->oz_allocated == rtg_blocks(oz->oz_rtg)) {
|
||||
spin_lock(&ip->i_flags_lock);
|
||||
oz = VFS_I(ip)->i_private;
|
||||
if (oz && oz->oz_allocated == rtg_blocks(oz->oz_rtg)) {
|
||||
VFS_I(ip)->i_private = NULL;
|
||||
spin_unlock(&ip->i_flags_lock);
|
||||
xfs_open_zone_put(oz);
|
||||
oz = NULL;
|
||||
goto out_unlock;
|
||||
}
|
||||
spin_unlock(&ip->i_flags_lock);
|
||||
}
|
||||
|
||||
if (!atomic_inc_not_zero(&oz->oz_ref))
|
||||
oz = NULL;
|
||||
out_unlock:
|
||||
rcu_read_unlock();
|
||||
return oz;
|
||||
}
|
||||
|
||||
|
|
@ -818,18 +836,41 @@ xfs_get_cached_zone(
|
|||
* that were every written to, but significantly simplifies the cached zone
|
||||
* lookup. Because the open_zone is clearly marked as full when all data
|
||||
* in the underlying RTG was written, the caching is always safe.
|
||||
*
|
||||
* Called with a reference on @oz held. And returns two references on the
|
||||
* returned zone: one for the caller and one for pinning the zone in
|
||||
* inode->i_private.
|
||||
*/
|
||||
static void
|
||||
static struct xfs_open_zone *
|
||||
xfs_set_cached_zone(
|
||||
struct xfs_inode *ip,
|
||||
struct xfs_open_zone *oz)
|
||||
{
|
||||
struct xfs_open_zone *old_oz;
|
||||
|
||||
/*
|
||||
* If the open zone cached in the inode still has free space, use that
|
||||
* instead of the new open zone just selected. This can happen when
|
||||
* multiple threads race to perform zone selection for an inode.
|
||||
* io_uring worker threads seem to be good way to trigger this.
|
||||
*
|
||||
* We need to grab an extra reference to this open zone as the caller
|
||||
* owns a reference in addition to the i_private pointer.
|
||||
*/
|
||||
spin_lock(&ip->i_flags_lock);
|
||||
old_oz = VFS_I(ip)->i_private;
|
||||
if (old_oz && old_oz->oz_allocated < rtg_blocks(old_oz->oz_rtg) &&
|
||||
atomic_inc_not_zero(&old_oz->oz_ref)) {
|
||||
spin_unlock(&ip->i_flags_lock);
|
||||
xfs_open_zone_put(oz);
|
||||
return old_oz;
|
||||
}
|
||||
VFS_I(ip)->i_private = oz;
|
||||
atomic_inc(&oz->oz_ref);
|
||||
old_oz = xchg(&VFS_I(ip)->i_private, oz);
|
||||
spin_unlock(&ip->i_flags_lock);
|
||||
if (old_oz)
|
||||
xfs_open_zone_put(old_oz);
|
||||
return oz;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -873,14 +914,13 @@ xfs_zone_alloc_and_submit(
|
|||
* the inode is still associated with a zone and use that if so.
|
||||
*/
|
||||
if (!*oz)
|
||||
*oz = xfs_get_cached_zone(ip);
|
||||
|
||||
if (!*oz) {
|
||||
select_zone:
|
||||
*oz = xfs_get_cached_zone(ip);
|
||||
if (!*oz) {
|
||||
*oz = xfs_select_zone(mp, write_hint, pack_tight);
|
||||
if (!*oz)
|
||||
goto out_error;
|
||||
xfs_set_cached_zone(ip, *oz);
|
||||
*oz = xfs_set_cached_zone(ip, *oz);
|
||||
}
|
||||
|
||||
alloc_len = xfs_zone_alloc_blocks(*oz, XFS_B_TO_FSB(mp, ioend->io_size),
|
||||
|
|
|
|||
|
|
@ -869,6 +869,11 @@ xfs_zone_gc_write_chunk(
|
|||
WRITE_ONCE(chunk->state, XFS_GC_BIO_NEW);
|
||||
list_move_tail(&chunk->entry, &data->writing);
|
||||
|
||||
/*
|
||||
* If we run on top of stacked block device, the read I/O might have
|
||||
* reset bi_bdev, restore it to the one we want.
|
||||
*/
|
||||
bio_set_dev(&chunk->bio, mp->m_rtdev_targp->bt_bdev);
|
||||
bio_reuse(&chunk->bio, REQ_OP_WRITE);
|
||||
while ((split_chunk = xfs_zone_gc_split_write(data, chunk)))
|
||||
xfs_zone_gc_submit_write(data, split_chunk);
|
||||
|
|
|
|||
|
|
@ -145,6 +145,7 @@ extern bool has_capability_noaudit(struct task_struct *t, int cap);
|
|||
extern bool has_ns_capability_noaudit(struct task_struct *t,
|
||||
struct user_namespace *ns, int cap);
|
||||
extern bool capable(int cap);
|
||||
bool capable_noaudit(int cap);
|
||||
extern bool ns_capable(struct user_namespace *ns, int cap);
|
||||
extern bool ns_capable_noaudit(struct user_namespace *ns, int cap);
|
||||
extern bool ns_capable_setid(struct user_namespace *ns, int cap);
|
||||
|
|
@ -167,6 +168,10 @@ static inline bool capable(int cap)
|
|||
{
|
||||
return true;
|
||||
}
|
||||
static inline bool capable_noaudit(int cap)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
static inline bool ns_capable(struct user_namespace *ns, int cap)
|
||||
{
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -326,7 +326,6 @@ bool has_capability_noaudit(struct task_struct *t, int cap)
|
|||
{
|
||||
return has_ns_capability_noaudit(t, &init_user_ns, cap);
|
||||
}
|
||||
EXPORT_SYMBOL(has_capability_noaudit);
|
||||
|
||||
static bool ns_capable_common(struct user_namespace *ns,
|
||||
int cap,
|
||||
|
|
@ -416,6 +415,24 @@ bool capable(int cap)
|
|||
return ns_capable(&init_user_ns, cap);
|
||||
}
|
||||
EXPORT_SYMBOL(capable);
|
||||
|
||||
/**
|
||||
* capable_noaudit - Determine if the current task has a superior
|
||||
* capability in effect by checking the process's effective
|
||||
* capabilities (unaudited).
|
||||
* @cap: The capability to be tested for
|
||||
*
|
||||
* This is the same as capable(), except it uses CAP_OPT_NOAUDIT as to prevent
|
||||
* issuing spurious audit messages.
|
||||
*
|
||||
* This sets PF_SUPERPRIV on the task if the capability is available on the
|
||||
* assumption that it's about to be used.
|
||||
*/
|
||||
bool capable_noaudit(int cap)
|
||||
{
|
||||
return ns_capable_noaudit(&init_user_ns, cap);
|
||||
}
|
||||
EXPORT_SYMBOL(capable_noaudit);
|
||||
#endif /* CONFIG_MULTIUSER */
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user