mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge branch 'fs-current' of linux-next
This commit is contained in:
+4
-1
@@ -477,6 +477,9 @@ void afs_fs_fetch_data(struct afs_operation *op)
|
||||
if (!call)
|
||||
return afs_op_nomem(op);
|
||||
|
||||
if (op->flags & AFS_OPERATION_ASYNC)
|
||||
call->async = true;
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = call->request;
|
||||
bp[0] = htonl(FSFETCHDATA);
|
||||
@@ -484,7 +487,7 @@ void afs_fs_fetch_data(struct afs_operation *op)
|
||||
bp[2] = htonl(vp->fid.vnode);
|
||||
bp[3] = htonl(vp->fid.unique);
|
||||
bp[4] = htonl(lower_32_bits(subreq->start + subreq->transferred));
|
||||
bp[5] = htonl(lower_32_bits(subreq->len + subreq->transferred));
|
||||
bp[5] = htonl(lower_32_bits(subreq->len - subreq->transferred));
|
||||
|
||||
call->fid = vp->fid;
|
||||
trace_afs_make_fs_call(call, &vp->fid);
|
||||
|
||||
+2
-1
@@ -1421,7 +1421,7 @@ static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *c
|
||||
{
|
||||
struct afs_addr_list *alist = op->estate->addresses;
|
||||
|
||||
op->call = call;
|
||||
op->call = afs_get_call(call, afs_call_trace_get);
|
||||
op->type = call->type;
|
||||
call->op = op;
|
||||
call->key = op->key;
|
||||
@@ -1429,6 +1429,7 @@ static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *c
|
||||
call->peer = rxrpc_kernel_get_peer(alist->addrs[op->addr_index].peer);
|
||||
call->service_id = op->server->service_id;
|
||||
afs_make_call(call, gfp);
|
||||
afs_put_call(call);
|
||||
}
|
||||
|
||||
static inline void afs_extract_begin(struct afs_call *call, void *buf, size_t size)
|
||||
|
||||
@@ -176,7 +176,7 @@ int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
* covered by the open-time check because sys_truncate() takes a
|
||||
* path, not an open file.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_FS_VERITY) && IS_VERITY(inode))
|
||||
if (IS_VERITY(inode))
|
||||
return -EPERM;
|
||||
|
||||
error = inode_newsize_ok(inode, attr->ia_size);
|
||||
|
||||
+34
-20
@@ -162,8 +162,10 @@ static Node *get_binfmt_handler(struct binfmt_misc *misc,
|
||||
static void put_binfmt_handler(Node *e)
|
||||
{
|
||||
if (refcount_dec_and_test(&e->users)) {
|
||||
if (e->flags & MISC_FMT_OPEN_FILE)
|
||||
if (e->flags & MISC_FMT_OPEN_FILE) {
|
||||
exe_file_allow_write_access(e->interp_file);
|
||||
filp_close(e->interp_file, NULL);
|
||||
}
|
||||
kfree(e);
|
||||
}
|
||||
}
|
||||
@@ -247,8 +249,14 @@ static int load_misc_binary(struct linux_binprm *bprm)
|
||||
|
||||
if (fmt->flags & MISC_FMT_OPEN_FILE) {
|
||||
interp_file = file_clone_open(fmt->interp_file);
|
||||
if (!IS_ERR(interp_file))
|
||||
deny_write_access(interp_file);
|
||||
if (!IS_ERR(interp_file)) {
|
||||
int err = exe_file_deny_write_access(interp_file);
|
||||
|
||||
if (err) {
|
||||
fput(interp_file);
|
||||
interp_file = ERR_PTR(err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
interp_file = open_exec(fmt->interpreter);
|
||||
}
|
||||
@@ -376,6 +384,10 @@ static Node *create_entry(const char __user *buffer, size_t count)
|
||||
|
||||
pr_debug("register: delim: %#x {%c}\n", del, del);
|
||||
|
||||
/* A flag-char delimiter runs the flag scan off the buffer. */
|
||||
if (del == 'P' || del == 'O' || del == 'C' || del == 'F')
|
||||
goto einval;
|
||||
|
||||
/* Pad the buffer with the delim to simplify parsing below. */
|
||||
memset(buf + count, del, 8);
|
||||
|
||||
@@ -909,18 +921,9 @@ static const struct file_operations bm_status_operations = {
|
||||
|
||||
/* Superblock handling */
|
||||
|
||||
static void bm_put_super(struct super_block *sb)
|
||||
{
|
||||
struct user_namespace *user_ns = sb->s_fs_info;
|
||||
|
||||
sb->s_fs_info = NULL;
|
||||
put_user_ns(user_ns);
|
||||
}
|
||||
|
||||
static const struct super_operations s_ops = {
|
||||
.statfs = simple_statfs,
|
||||
.evict_inode = bm_evict_inode,
|
||||
.put_super = bm_put_super,
|
||||
};
|
||||
|
||||
static int bm_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
@@ -937,6 +940,10 @@ static int bm_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
if (WARN_ON(user_ns != current_user_ns()))
|
||||
return -EINVAL;
|
||||
|
||||
/* Never exec off this instance and never let anything stack on it. */
|
||||
sb->s_iflags |= SB_I_NOEXEC | SB_I_NODEV;
|
||||
sb->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH;
|
||||
|
||||
/*
|
||||
* Lazily allocate a new binfmt_misc instance for this namespace, i.e.
|
||||
* do it here during the first mount of binfmt_misc. We don't need to
|
||||
@@ -974,13 +981,12 @@ static int bm_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
/*
|
||||
* When the binfmt_misc superblock for this userns is shutdown
|
||||
* ->enabled might have been set to false and we don't reinitialize
|
||||
* ->enabled again in put_super() as someone might already be mounting
|
||||
* binfmt_misc again. It also would be pointless since by the time
|
||||
* ->put_super() is called we know that the binary type list for this
|
||||
* bintfmt_misc mount is empty making load_misc_binary() return
|
||||
* -ENOEXEC independent of whether ->enabled is true. Instead, if
|
||||
* someone mounts binfmt_misc for the first time or again we simply
|
||||
* reset ->enabled to true.
|
||||
* ->enabled again during shutdown as someone might already be mounting
|
||||
* binfmt_misc again. It also would be pointless since by then we know
|
||||
* that the binary type list for this binfmt_misc mount is empty making
|
||||
* load_misc_binary() return -ENOEXEC independent of whether ->enabled
|
||||
* is true. Instead, if someone mounts binfmt_misc for the first time or
|
||||
* again we simply reset ->enabled to true.
|
||||
*/
|
||||
misc->enabled = true;
|
||||
|
||||
@@ -1006,6 +1012,14 @@ static const struct fs_context_operations bm_context_ops = {
|
||||
.get_tree = bm_get_tree,
|
||||
};
|
||||
|
||||
static void bm_kill_sb(struct super_block *sb)
|
||||
{
|
||||
struct user_namespace *user_ns = sb->s_fs_info;
|
||||
|
||||
kill_anon_super(sb);
|
||||
put_user_ns(user_ns);
|
||||
}
|
||||
|
||||
static int bm_init_fs_context(struct fs_context *fc)
|
||||
{
|
||||
fc->ops = &bm_context_ops;
|
||||
@@ -1022,7 +1036,7 @@ static struct file_system_type bm_fs_type = {
|
||||
.name = "binfmt_misc",
|
||||
.init_fs_context = bm_init_fs_context,
|
||||
.fs_flags = FS_USERNS_MOUNT,
|
||||
.kill_sb = kill_anon_super,
|
||||
.kill_sb = bm_kill_sb,
|
||||
};
|
||||
MODULE_ALIAS_FS("binfmt_misc");
|
||||
|
||||
|
||||
@@ -600,6 +600,7 @@ int btrfs_prealloc_file_range_trans(struct inode *inode,
|
||||
loff_t actual_len, u64 *alloc_hint);
|
||||
int btrfs_run_delalloc_range(struct btrfs_inode *inode, struct folio *locked_folio,
|
||||
u64 start, u64 end, struct writeback_control *wbc);
|
||||
void btrfs_queue_writepage_fixup(struct btrfs_inode *inode, struct folio *folio);
|
||||
int btrfs_encoded_io_compression_from_extent(struct btrfs_fs_info *fs_info,
|
||||
int compress_type);
|
||||
int btrfs_encoded_read_regular_fill_pages(struct btrfs_inode *inode,
|
||||
|
||||
+27
-1
@@ -1760,6 +1760,8 @@ static int read_backup_root(struct btrfs_fs_info *fs_info, u8 priority)
|
||||
/* helper to cleanup workers */
|
||||
static void btrfs_stop_all_workers(struct btrfs_fs_info *fs_info)
|
||||
{
|
||||
if (fs_info->fixup_workers)
|
||||
destroy_workqueue(fs_info->fixup_workers);
|
||||
btrfs_destroy_workqueue(fs_info->delalloc_workers);
|
||||
btrfs_destroy_workqueue(fs_info->workers);
|
||||
if (fs_info->endio_workers)
|
||||
@@ -1967,6 +1969,9 @@ static int btrfs_init_workqueues(struct btrfs_fs_info *fs_info)
|
||||
fs_info->caching_workers =
|
||||
btrfs_alloc_workqueue(fs_info, "cache", flags, max_active, 0);
|
||||
|
||||
fs_info->fixup_workers =
|
||||
alloc_ordered_workqueue("btrfs-fixup", ordered_flags);
|
||||
|
||||
fs_info->endio_workers =
|
||||
alloc_workqueue("btrfs-endio", flags, max_active);
|
||||
fs_info->endio_meta_workers =
|
||||
@@ -1992,7 +1997,7 @@ static int btrfs_init_workqueues(struct btrfs_fs_info *fs_info)
|
||||
fs_info->endio_workers && fs_info->endio_meta_workers &&
|
||||
fs_info->endio_write_workers &&
|
||||
fs_info->endio_freespace_worker && fs_info->rmw_workers &&
|
||||
fs_info->caching_workers &&
|
||||
fs_info->caching_workers && fs_info->fixup_workers &&
|
||||
fs_info->delayed_workers && fs_info->qgroup_rescan_workers &&
|
||||
fs_info->discard_ctl.discard_workers)) {
|
||||
return -ENOMEM;
|
||||
@@ -4356,6 +4361,18 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info)
|
||||
/* clear out the rbtree of defraggable inodes */
|
||||
btrfs_cleanup_defrag_inodes(fs_info);
|
||||
|
||||
/*
|
||||
* Before the unmount, we sync down all the writeback which can
|
||||
* generate fixup work. We are about to run delalloc for autodefrag so
|
||||
* piggy back on that by also flushing the fixup work which can also
|
||||
* generate delalloc we would like to get run.
|
||||
*
|
||||
* After this, it is still possible that some thread doing writeback is
|
||||
* in btrfs_queue_writepage_fixup() and might finish queueing some final
|
||||
* work, racing the btrfs_fs_closing() check there.
|
||||
*/
|
||||
flush_workqueue(fs_info->fixup_workers);
|
||||
|
||||
/*
|
||||
* Handle the error fs first, as it will flush and wait for all ordered
|
||||
* extents. This will generate delayed iputs, thus we want to handle
|
||||
@@ -4433,6 +4450,15 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info)
|
||||
cancel_work_sync(&fs_info->preempt_reclaim_work);
|
||||
cancel_work_sync(&fs_info->em_shrinker_work);
|
||||
|
||||
/*
|
||||
* Reclaim workers can run writeback which can queue fixup.
|
||||
* After the above cancel_work_sync() calls, any such queueing attempts are
|
||||
* guaranteed to see btrfs_fs_closing(), so at this point we can genuinely fully
|
||||
* flush the fixup workqueue. This relies on the belief that *now* no thread can
|
||||
* still be sitting in btrfs_queue_writepage_fixup().
|
||||
*/
|
||||
flush_workqueue(fs_info->fixup_workers);
|
||||
|
||||
/*
|
||||
* Run delayed iputs again because an async reclaim worker may have
|
||||
* added new ones if it was flushing delalloc:
|
||||
|
||||
@@ -1440,6 +1440,115 @@ static bool find_next_delalloc_bitmap(struct folio *folio,
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Debug checks for fixup selection logic to help ensure the invariants
|
||||
* we expect for fixup marking hold in practice.
|
||||
*
|
||||
* - A dirty block without a fixup bit is covered by delalloc or a running
|
||||
* ordered extent (it was dirtied by a reserving write path).
|
||||
* - A block with a fixup bit is never covered by delalloc: every delalloc
|
||||
* setter holds the folio lock and cancels the fixup state of the blocks
|
||||
* it covers (btrfs_folio_set_dirty()) before releasing it.
|
||||
*/
|
||||
static void debug_check_writepage_fixup(struct btrfs_inode *inode, u64 start,
|
||||
u32 len, bool needs_fixup)
|
||||
{
|
||||
struct btrfs_ordered_extent *ordered;
|
||||
bool delalloc;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_BTRFS_DEBUG))
|
||||
return;
|
||||
|
||||
delalloc = btrfs_test_range_bit_exists(&inode->io_tree, start,
|
||||
start + len - 1, EXTENT_DELALLOC);
|
||||
if (needs_fixup) {
|
||||
if (unlikely(delalloc))
|
||||
DEBUG_WARN("writeback: delalloc and fixup conflict. ino %llu start %llu",
|
||||
btrfs_ino(inode), start);
|
||||
} else {
|
||||
if (delalloc)
|
||||
return;
|
||||
|
||||
ordered = btrfs_lookup_ordered_range(inode, start, len);
|
||||
if (unlikely(!ordered))
|
||||
DEBUG_WARN("dirty block, no delalloc, fixup, ordered. ino %llu start %llu",
|
||||
btrfs_ino(inode), start);
|
||||
else
|
||||
btrfs_put_ordered_extent(ordered);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle folios dirtied without a delalloc reservation, e.g.
|
||||
* O_DIRECT read into a MAP_SHARED mapping dirtying via set_page_dirty_lock().
|
||||
*
|
||||
* btrfs_data_dirty_folio() records the affected blocks in the fixup bitmap
|
||||
* and the folio fixup flag and we check them here in writeback.
|
||||
*
|
||||
* Don't submit such blocks and queue work for the fixup worker to reserve
|
||||
* space for them so that they can be submitted properly by writeback.
|
||||
*
|
||||
* Return 1 if the folio needed fixup, 0 if not, and a negative error code
|
||||
* on error.
|
||||
*/
|
||||
static noinline_for_stack int writepage_fixup(struct btrfs_inode *inode,
|
||||
struct folio *folio,
|
||||
struct btrfs_bio_ctrl *bio_ctrl)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = inode_to_fs_info(&inode->vfs_inode);
|
||||
const unsigned int blocks_per_folio = btrfs_blocks_per_folio(fs_info, folio);
|
||||
const u32 sectorsize = fs_info->sectorsize;
|
||||
const u64 page_start = folio_pos(folio);
|
||||
bool found_fixup = false;
|
||||
unsigned int bit;
|
||||
|
||||
/*
|
||||
* A folio was dirtied without calling aops->dirty_folio() which we
|
||||
* explicitly assert is not allowed.
|
||||
*/
|
||||
if (unlikely(bitmap_empty(bio_ctrl->submit_bitmap, blocks_per_folio))) {
|
||||
DEBUG_WARN();
|
||||
btrfs_err_rl(fs_info,
|
||||
"root %lld ino %llu folio %llu is dirty with an empty dirty bitmap",
|
||||
btrfs_root_id(inode->root), btrfs_ino(inode),
|
||||
folio_pos(folio));
|
||||
return -EUCLEAN;
|
||||
}
|
||||
|
||||
/* Cheap check on the folio flag. Set iff the fixup bitmap is non-empty. */
|
||||
if (likely(!folio_test_fixup_pending(folio)))
|
||||
return 0;
|
||||
|
||||
for_each_set_bit(bit, bio_ctrl->submit_bitmap, blocks_per_folio) {
|
||||
const u64 start = page_start + (bit << fs_info->sectorsize_bits);
|
||||
const bool needs_fixup = btrfs_folio_test_fixup(fs_info, folio,
|
||||
start, sectorsize);
|
||||
|
||||
debug_check_writepage_fixup(inode, start, sectorsize, needs_fixup);
|
||||
if (needs_fixup) {
|
||||
bitmap_clear(bio_ctrl->submit_bitmap, bit, 1);
|
||||
found_fixup = true;
|
||||
}
|
||||
}
|
||||
if (likely(found_fixup)) {
|
||||
btrfs_queue_writepage_fixup(inode, folio);
|
||||
folio_redirty_for_writepage(bio_ctrl->wbc, folio);
|
||||
if (bitmap_empty(bio_ctrl->submit_bitmap, blocks_per_folio)) {
|
||||
folio_unlock(folio);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* We should always find fixup if the folio fixup flag was set. */
|
||||
DEBUG_WARN();
|
||||
btrfs_err_rl(fs_info,
|
||||
"root %lld ino %llu folio %llu is fixup with an empty fixup bitmap",
|
||||
btrfs_root_id(inode->root), btrfs_ino(inode),
|
||||
folio_pos(folio));
|
||||
|
||||
return -EUCLEAN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do all of the delayed allocation setup.
|
||||
*
|
||||
@@ -1492,6 +1601,10 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode,
|
||||
/* Save the dirty bitmap as our submission bitmap will be a subset of it. */
|
||||
btrfs_copy_subpage_dirty_bitmap(fs_info, folio, bio_ctrl->submit_bitmap);
|
||||
|
||||
ret = writepage_fixup(inode, folio, bio_ctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for_each_set_bitrange(start_bit, end_bit, bio_ctrl->submit_bitmap,
|
||||
blocks_per_folio) {
|
||||
u64 start = page_start + (start_bit << fs_info->sectorsize_bits);
|
||||
|
||||
@@ -713,6 +713,8 @@ struct btrfs_fs_info {
|
||||
struct btrfs_workqueue *endio_write_workers;
|
||||
struct btrfs_workqueue *endio_freespace_worker;
|
||||
struct btrfs_workqueue *caching_workers;
|
||||
|
||||
struct workqueue_struct *fixup_workers;
|
||||
struct btrfs_workqueue *delayed_workers;
|
||||
|
||||
struct task_struct *transaction_kthread;
|
||||
@@ -1200,6 +1202,16 @@ static inline void btrfs_wake_unfinished_drop(struct btrfs_fs_info *fs_info)
|
||||
clear_and_wake_up_bit(BTRFS_FS_UNFINISHED_DROPS, &fs_info->flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* We use the folio owner_2 flag to indicate the folio has blocks that were
|
||||
* dirtied without a space reservation and need the writepage fixup before
|
||||
* writeback. For bs < folio_size the fixup bitmap tracks the affected
|
||||
* blocks.
|
||||
*/
|
||||
#define folio_test_fixup_pending(folio) folio_test_owner_2(folio)
|
||||
#define folio_set_fixup_pending(folio) folio_set_owner_2(folio)
|
||||
#define folio_clear_fixup_pending(folio) folio_clear_owner_2(folio)
|
||||
|
||||
#define BTRFS_FS_ERROR(fs_info) (READ_ONCE((fs_info)->fs_error))
|
||||
|
||||
#define BTRFS_FS_LOG_CLEANUP_ERROR(fs_info) \
|
||||
|
||||
+216
-1
@@ -2812,6 +2812,180 @@ int btrfs_set_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end,
|
||||
EXTENT_DELALLOC | extra_bits, cached_state);
|
||||
}
|
||||
|
||||
struct btrfs_writepage_fixup {
|
||||
struct folio *folio;
|
||||
struct btrfs_inode *inode;
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
/*
|
||||
* Do the real fixup work of reserving space for the blocks a folio's fixup
|
||||
* state records. Queued by writepage_fixup() when writeback found the bits set.
|
||||
*
|
||||
* Since the fixup can be cancelled by a task dirtying with a reservation, we must
|
||||
* re-check the state of fixup under the folio lock.
|
||||
*/
|
||||
static void btrfs_writepage_fixup_worker(struct work_struct *work)
|
||||
{
|
||||
struct btrfs_writepage_fixup *fixup =
|
||||
container_of(work, struct btrfs_writepage_fixup, work);
|
||||
struct extent_state *cached_state = NULL;
|
||||
struct extent_changeset *data_reserved = NULL;
|
||||
unsigned long delalloc_bitmap[BITS_TO_LONGS(BTRFS_MAX_BLOCKS_PER_FOLIO)] = { 0 };
|
||||
struct folio *folio = fixup->folio;
|
||||
struct btrfs_inode *inode = fixup->inode;
|
||||
struct btrfs_fs_info *fs_info = inode->root->fs_info;
|
||||
const unsigned int blocks_per_folio = btrfs_blocks_per_folio(fs_info, folio);
|
||||
const u32 sectorsize = fs_info->sectorsize;
|
||||
const u64 page_start = folio_pos(folio);
|
||||
const u64 page_end = folio_next_pos(folio) - 1;
|
||||
unsigned int start_bit;
|
||||
unsigned int end_bit;
|
||||
unsigned int bit;
|
||||
bool reserved;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* We would prefer to reserve under the folio lock when we know exactly
|
||||
* which blocks need a reservation. Unfortunately, since the reservation
|
||||
* can go into flushers which can go into writeback, which takes folio
|
||||
* locks, that is not possible. Therefore, we have to reserve for the
|
||||
* whole folio here, then release what we didn't end up needing once we
|
||||
* figure it out.
|
||||
*
|
||||
* Also note the slightly strange error checking. If fixup is actually
|
||||
* not set, we don't need to mark an error on the mapping. So hang on to
|
||||
* ret until after we lock and find out if we actually care.
|
||||
*/
|
||||
ret = btrfs_delalloc_reserve_space(inode, &data_reserved, page_start,
|
||||
folio_size(folio));
|
||||
reserved = (ret == 0);
|
||||
again:
|
||||
folio_lock(folio);
|
||||
|
||||
if (!folio->mapping || !folio_test_fixup_pending(folio)) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
btrfs_lock_extent(&inode->io_tree, page_start, page_end, &cached_state);
|
||||
|
||||
for (bit = 0; bit < blocks_per_folio; bit++) {
|
||||
struct btrfs_ordered_extent *ordered;
|
||||
const u64 start = page_start + (bit << fs_info->sectorsize_bits);
|
||||
|
||||
if (test_bit(bit, delalloc_bitmap))
|
||||
continue;
|
||||
if (!btrfs_folio_test_fixup(fs_info, folio, start, sectorsize))
|
||||
continue;
|
||||
/*
|
||||
* Any task that sets EXTENT_DELALLOC clears the fixup bits
|
||||
* under the folio lock, so it should be impossible to observe
|
||||
* both under the lock. Setting delalloc twice would wrongly
|
||||
* double account the space.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_BTRFS_DEBUG) &&
|
||||
unlikely(btrfs_test_range_bit_exists(&inode->io_tree, start,
|
||||
start + sectorsize - 1,
|
||||
EXTENT_DELALLOC))) {
|
||||
DEBUG_WARN("fixup worker: delalloc and fixup conflict. ino %llu start %llu",
|
||||
btrfs_ino(inode), start);
|
||||
btrfs_folio_clear_fixup(fs_info, folio, start, sectorsize);
|
||||
continue;
|
||||
}
|
||||
ordered = btrfs_lookup_ordered_range(inode, start, sectorsize);
|
||||
if (ordered) {
|
||||
trace_btrfs_writepage_fixup_defer(inode, ordered);
|
||||
btrfs_unlock_extent(&inode->io_tree, page_start,
|
||||
page_end, &cached_state);
|
||||
folio_unlock(folio);
|
||||
btrfs_start_ordered_extent(ordered);
|
||||
btrfs_put_ordered_extent(ordered);
|
||||
goto again;
|
||||
}
|
||||
ret = btrfs_set_extent_delalloc(inode, start,
|
||||
start + sectorsize - 1, 0,
|
||||
&cached_state);
|
||||
if (ret)
|
||||
break;
|
||||
trace_btrfs_writepage_fixup_reserve(inode, start, sectorsize);
|
||||
btrfs_folio_clear_fixup(fs_info, folio, start, sectorsize);
|
||||
set_bit(bit, delalloc_bitmap);
|
||||
}
|
||||
|
||||
btrfs_unlock_extent(&inode->io_tree, page_start, page_end, &cached_state);
|
||||
out:
|
||||
if (ret < 0) {
|
||||
/* Failure here is analogous to failure in writeback. */
|
||||
mapping_set_error(folio->mapping, ret);
|
||||
btrfs_folio_clear_fixup_dirty(fs_info, folio, page_start,
|
||||
folio_size(folio));
|
||||
}
|
||||
if (reserved) {
|
||||
btrfs_delalloc_release_extents(inode, folio_size(folio));
|
||||
for_each_clear_bitrange(start_bit, end_bit, delalloc_bitmap,
|
||||
blocks_per_folio)
|
||||
btrfs_delalloc_release_space(inode, data_reserved,
|
||||
page_start + (start_bit << fs_info->sectorsize_bits),
|
||||
(end_bit - start_bit) << fs_info->sectorsize_bits,
|
||||
true);
|
||||
}
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
kfree(fixup);
|
||||
extent_changeset_free(data_reserved);
|
||||
btrfs_add_delayed_iput(inode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Queue space reservation fixup work for blocks dirtied without a space reservation.
|
||||
*
|
||||
* Should be used by writeback while holding the folio locked.
|
||||
*
|
||||
* If we fail to queue fixup, then the folio state is unchanged and a future
|
||||
* writeback pass will still see it.
|
||||
*/
|
||||
void btrfs_queue_writepage_fixup(struct btrfs_inode *inode, struct folio *folio)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = inode->root->fs_info;
|
||||
struct btrfs_writepage_fixup *fixup;
|
||||
|
||||
/*
|
||||
* Disallow queueing more fixup during unmount to break the cycle
|
||||
* of writeback queuing fixup queuing writeback etc.
|
||||
*
|
||||
* If it actually hit, then something which was fixup wasn't written
|
||||
* which we should warn about.
|
||||
*/
|
||||
if (btrfs_fs_closing(fs_info)) {
|
||||
btrfs_warn_rl(fs_info,
|
||||
"dropping unqueued fixup blocks at unmount. root %lld ino %llu folio %llu",
|
||||
btrfs_root_id(inode->root), btrfs_ino(inode),
|
||||
folio_pos(folio));
|
||||
btrfs_folio_clear_fixup_dirty(fs_info, folio,
|
||||
folio_pos(folio), folio_size(folio));
|
||||
return;
|
||||
}
|
||||
|
||||
fixup = kzalloc_obj(*fixup, GFP_NOFS);
|
||||
if (!fixup)
|
||||
return;
|
||||
|
||||
/*
|
||||
* This is called from within extent_write_cache_pages() which
|
||||
* has successfully done an igrab(). But that will be released at the
|
||||
* end of the writeback pass. We need to extend it for the worker as well.
|
||||
*/
|
||||
ihold(&inode->vfs_inode);
|
||||
folio_get(folio);
|
||||
INIT_WORK(&fixup->work, btrfs_writepage_fixup_worker);
|
||||
fixup->folio = folio;
|
||||
fixup->inode = inode;
|
||||
queue_work(fs_info->fixup_workers, &fixup->work);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the old accounting flags and set EXTENT_DELALLOC for the range.
|
||||
*
|
||||
@@ -7507,6 +7681,12 @@ static void btrfs_invalidate_folio(struct folio *folio, size_t offset,
|
||||
folio_wait_writeback(folio);
|
||||
wait_subpage_spinlock(folio);
|
||||
|
||||
/*
|
||||
* The invalidated blocks are going away; drop any fixup blocks among
|
||||
* them, data included, as they have no space reservation.
|
||||
*/
|
||||
btrfs_folio_clear_fixup_dirty(fs_info, folio, page_start + offset, length);
|
||||
|
||||
/*
|
||||
* For subpage case, we have call sites like
|
||||
* btrfs_punch_hole_lock_range() which passes range not aligned to
|
||||
@@ -10548,6 +10728,41 @@ static const struct file_operations btrfs_dir_file_operations = {
|
||||
.setlease = generic_setlease,
|
||||
};
|
||||
|
||||
/*
|
||||
* The folio is going dirty without a btrfs delalloc space reservation.
|
||||
* This requires a fixup before writeback which we might sleep so cannot
|
||||
* run in this context, so we merely set state on the folio indicating it
|
||||
* needs fixup before writeback.
|
||||
*
|
||||
* Note that there is no range in the input, so the whole folio is marked
|
||||
* dirty and fixup.
|
||||
*
|
||||
* We believe that all callers of dirty_folio either:
|
||||
* - take the folio lock (e.g. pinned folio release notification).
|
||||
* - take the pte lock but must be running on a dirty pte which means
|
||||
* page_mkwrite() ran on it and reserved the space. zap_pte_range() cannot
|
||||
* race with writeback cleaning the folio because writeback runs
|
||||
* folio_mkclean() which also uses the pte lock and revokes outstanding
|
||||
* writable mappings.
|
||||
* Therefore, an additional folio private lock (a la bfs->lock for all cases,
|
||||
* not just subpage) is not necessary.
|
||||
*/
|
||||
static bool btrfs_data_dirty_folio(struct address_space *mapping,
|
||||
struct folio *folio)
|
||||
{
|
||||
struct btrfs_inode *inode = BTRFS_I(mapping->host);
|
||||
struct btrfs_fs_info *fs_info = inode->root->fs_info;
|
||||
const u64 page_start = folio_pos(folio);
|
||||
const u64 range_end = min_t(u64, folio_next_pos(folio),
|
||||
round_up(i_size_read(&inode->vfs_inode),
|
||||
fs_info->sectorsize));
|
||||
|
||||
if (range_end > page_start)
|
||||
btrfs_folio_set_fixup_dirty(fs_info, folio, page_start,
|
||||
range_end - page_start);
|
||||
return filemap_dirty_folio(mapping, folio);
|
||||
}
|
||||
|
||||
/*
|
||||
* btrfs doesn't support the bmap operation because swapfiles
|
||||
* use bmap to make a mapping of extents in the file. They assume
|
||||
@@ -10568,7 +10783,7 @@ static const struct address_space_operations btrfs_aops = {
|
||||
.launder_folio = btrfs_launder_folio,
|
||||
.release_folio = btrfs_release_folio,
|
||||
.migrate_folio = btrfs_migrate_folio,
|
||||
.dirty_folio = filemap_dirty_folio,
|
||||
.dirty_folio = btrfs_data_dirty_folio,
|
||||
.error_remove_folio = generic_error_remove_folio,
|
||||
.swap_activate = btrfs_swap_activate,
|
||||
.swap_deactivate = btrfs_swap_deactivate,
|
||||
|
||||
+211
-5
@@ -345,18 +345,57 @@ void btrfs_subpage_clear_uptodate(const struct btrfs_fs_info *fs_info,
|
||||
spin_unlock_irqrestore(&bfs->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* folio_mark_dirty() for a folio we are dirtying with a space reservation.
|
||||
*
|
||||
* Dirtiers without a reservation use btrfs_data_dirty_folio().
|
||||
*/
|
||||
static void btrfs_folio_mark_dirty(struct folio *folio)
|
||||
{
|
||||
struct address_space *mapping = folio_mapping(folio);
|
||||
|
||||
if (!mapping || !mapping->host || !is_data_inode(BTRFS_I(mapping->host))) {
|
||||
folio_mark_dirty(folio);
|
||||
return;
|
||||
}
|
||||
if (folio_test_reclaim(folio))
|
||||
folio_clear_reclaim(folio);
|
||||
filemap_dirty_folio(mapping, folio);
|
||||
}
|
||||
|
||||
/*
|
||||
* The set helper of the dirty ops, so it only runs for folios without a
|
||||
* fixup bitmap: for those the folio flag is the whole fixup state, and this
|
||||
* reserving write covers the block, so retire it. Metadata never has the
|
||||
* flag set and only pays the test.
|
||||
*/
|
||||
static void btrfs_folio_mark_dirty_reserved(struct folio *folio)
|
||||
{
|
||||
if (folio_test_fixup_pending(folio))
|
||||
folio_clear_fixup_pending(folio);
|
||||
btrfs_folio_mark_dirty(folio);
|
||||
}
|
||||
|
||||
void btrfs_subpage_set_dirty(const struct btrfs_fs_info *fs_info,
|
||||
struct folio *folio, u64 start, u32 len)
|
||||
{
|
||||
struct btrfs_folio_state *bfs = folio_get_private(folio);
|
||||
unsigned int start_bit = subpage_calc_start_bit(fs_info, folio,
|
||||
unsigned int dirty_bit = subpage_calc_start_bit(fs_info, folio,
|
||||
dirty, start, len);
|
||||
unsigned int fixup_bit = subpage_calc_start_bit(fs_info, folio,
|
||||
fixup, start, len);
|
||||
const unsigned int nbits = len >> fs_info->sectorsize_bits;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&bfs->lock, flags);
|
||||
bitmap_set(bfs->bitmaps, start_bit, len >> fs_info->sectorsize_bits);
|
||||
bitmap_set(bfs->bitmaps, dirty_bit, nbits);
|
||||
/* Proper dirtying obviates the need for fixup. */
|
||||
bitmap_clear(bfs->bitmaps, fixup_bit, nbits);
|
||||
if (folio_test_fixup_pending(folio) &&
|
||||
subpage_test_bitmap_all_zero(fs_info, folio, fixup))
|
||||
folio_clear_fixup_pending(folio);
|
||||
spin_unlock_irqrestore(&bfs->lock, flags);
|
||||
folio_mark_dirty(folio);
|
||||
btrfs_folio_mark_dirty(folio);
|
||||
}
|
||||
|
||||
static void folio_clear_tags(struct folio *folio)
|
||||
@@ -457,6 +496,172 @@ void btrfs_subpage_clear_writeback(const struct btrfs_fs_info *fs_info,
|
||||
spin_unlock_irqrestore(&bfs->lock, flags);
|
||||
}
|
||||
|
||||
void btrfs_subpage_clear_fixup(const struct btrfs_fs_info *fs_info,
|
||||
struct folio *folio, u64 start, u32 len)
|
||||
{
|
||||
struct btrfs_folio_state *bfs = folio_get_private(folio);
|
||||
unsigned int start_bit = subpage_calc_start_bit(fs_info, folio,
|
||||
fixup, start, len);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&bfs->lock, flags);
|
||||
bitmap_clear(bfs->bitmaps, start_bit, len >> fs_info->sectorsize_bits);
|
||||
if (subpage_test_bitmap_all_zero(fs_info, folio, fixup))
|
||||
folio_clear_fixup_pending(folio);
|
||||
spin_unlock_irqrestore(&bfs->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* In one pass under bfs->lock, mark every block with a clear dirty bit in the
|
||||
* range both dirty and needing fixup.
|
||||
*
|
||||
* Only called from the dirty_folio callback, which owns the folio-level
|
||||
* dirty flag; calling folio_mark_dirty() here would recurse.
|
||||
*
|
||||
* The folio fixup flag and bits are both set under bfs->lock so that a
|
||||
* writeback pass observing the new bits also observes the flag.
|
||||
*/
|
||||
static void btrfs_subpage_set_fixup_dirty(const struct btrfs_fs_info *fs_info,
|
||||
struct folio *folio, u64 start, u32 len)
|
||||
{
|
||||
struct btrfs_folio_state *bfs = folio_get_private(folio);
|
||||
unsigned int dirty_bit = subpage_calc_start_bit(fs_info, folio,
|
||||
dirty, start, len);
|
||||
unsigned int fixup_bit = subpage_calc_start_bit(fs_info, folio,
|
||||
fixup, start, len);
|
||||
const unsigned int nbits = len >> fs_info->sectorsize_bits;
|
||||
unsigned long flags;
|
||||
bool marked = false;
|
||||
|
||||
spin_lock_irqsave(&bfs->lock, flags);
|
||||
for (unsigned int i = 0; i < nbits; i++) {
|
||||
if (test_bit(dirty_bit + i, bfs->bitmaps))
|
||||
continue;
|
||||
set_bit(dirty_bit + i, bfs->bitmaps);
|
||||
set_bit(fixup_bit + i, bfs->bitmaps);
|
||||
marked = true;
|
||||
}
|
||||
if (marked)
|
||||
folio_set_fixup_pending(folio);
|
||||
spin_unlock_irqrestore(&bfs->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark the still-clean blocks of a folio dirty and needing fixup, for
|
||||
* btrfs_data_dirty_folio().
|
||||
*
|
||||
* A subpage block size folio that is not uptodate is left alone: its clean
|
||||
* blocks may hold content that was never read in, which must not be marked
|
||||
* dirty.
|
||||
*/
|
||||
void btrfs_folio_set_fixup_dirty(const struct btrfs_fs_info *fs_info,
|
||||
struct folio *folio, u64 start, u32 len)
|
||||
{
|
||||
if (!btrfs_is_subpage(fs_info, folio)) {
|
||||
if (!folio_test_dirty(folio))
|
||||
folio_set_fixup_pending(folio);
|
||||
return;
|
||||
}
|
||||
if (!folio_test_uptodate(folio))
|
||||
return;
|
||||
btrfs_subpage_set_fixup_dirty(fs_info, folio, start, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Drop the fixup blocks inside the range: clear both their fixup and dirty
|
||||
* bits.
|
||||
*
|
||||
* Fixup blocks carry no space reservation, so their fixup and dirty bits
|
||||
* must be dropped together. Clearing only the fixup bit would leave a
|
||||
* dirty block without a reservation which is not a valid state.
|
||||
*
|
||||
* Returns true if the folio has no dirty blocks left.
|
||||
*/
|
||||
static bool btrfs_subpage_clear_fixup_dirty(const struct btrfs_fs_info *fs_info,
|
||||
struct folio *folio, u64 start, u32 len)
|
||||
{
|
||||
struct btrfs_folio_state *bfs = folio_get_private(folio);
|
||||
unsigned int dirty_bit = subpage_calc_start_bit(fs_info, folio,
|
||||
dirty, start, len);
|
||||
unsigned int fixup_bit = subpage_calc_start_bit(fs_info, folio,
|
||||
fixup, start, len);
|
||||
const unsigned int nbits = len >> fs_info->sectorsize_bits;
|
||||
unsigned long flags;
|
||||
bool last;
|
||||
|
||||
spin_lock_irqsave(&bfs->lock, flags);
|
||||
for (unsigned int i = 0; i < nbits; i++) {
|
||||
if (!test_bit(fixup_bit + i, bfs->bitmaps))
|
||||
continue;
|
||||
clear_bit(fixup_bit + i, bfs->bitmaps);
|
||||
clear_bit(dirty_bit + i, bfs->bitmaps);
|
||||
}
|
||||
if (subpage_test_bitmap_all_zero(fs_info, folio, fixup))
|
||||
folio_clear_fixup_pending(folio);
|
||||
last = subpage_test_bitmap_all_zero(fs_info, folio, dirty);
|
||||
spin_unlock_irqrestore(&bfs->lock, flags);
|
||||
return last;
|
||||
}
|
||||
|
||||
/*
|
||||
* Drop the fixup blocks inside the range, for callers discarding their data:
|
||||
* btrfs_invalidate_folio() and the writepage fixup worker's error path.
|
||||
*
|
||||
* Callers that have just reserved space for a block want
|
||||
* btrfs_folio_clear_fixup() instead - there the block stays dirty and gets
|
||||
* written.
|
||||
*
|
||||
* The range can be byte-granular (an unaligned truncate through
|
||||
* btrfs_invalidate_folio()); only blocks fully inside it are dropped, as a
|
||||
* partially covered block still holds live data outside the range. For
|
||||
* single-block folios the folio flag is the fixup state, so it is dropped
|
||||
* only when the range covers the whole folio.
|
||||
*/
|
||||
void btrfs_folio_clear_fixup_dirty(const struct btrfs_fs_info *fs_info,
|
||||
struct folio *folio, u64 start, u32 len)
|
||||
{
|
||||
u64 aligned_start;
|
||||
u64 aligned_end;
|
||||
|
||||
/* The folio flag is set whenever any fixup bitmap bit is. */
|
||||
if (!folio_test_fixup_pending(folio))
|
||||
return;
|
||||
if (!btrfs_is_subpage(fs_info, folio)) {
|
||||
if (start <= folio_pos(folio) &&
|
||||
start + len >= folio_next_pos(folio)) {
|
||||
folio_clear_fixup_pending(folio);
|
||||
folio_clear_dirty_for_io(folio);
|
||||
}
|
||||
return;
|
||||
}
|
||||
btrfs_subpage_clamp_range(folio, &start, &len);
|
||||
aligned_start = round_up(start, fs_info->sectorsize);
|
||||
aligned_end = round_down(start + len, fs_info->sectorsize);
|
||||
if (aligned_end <= aligned_start)
|
||||
return;
|
||||
if (btrfs_subpage_clear_fixup_dirty(fs_info, folio, aligned_start,
|
||||
aligned_end - aligned_start))
|
||||
folio_clear_dirty_for_io(folio);
|
||||
}
|
||||
|
||||
bool btrfs_folio_test_fixup(const struct btrfs_fs_info *fs_info,
|
||||
struct folio *folio, u64 start, u32 len)
|
||||
{
|
||||
if (!btrfs_is_subpage(fs_info, folio))
|
||||
return folio_test_fixup_pending(folio);
|
||||
return btrfs_subpage_test_fixup(fs_info, folio, start, len);
|
||||
}
|
||||
|
||||
void btrfs_folio_clear_fixup(const struct btrfs_fs_info *fs_info,
|
||||
struct folio *folio, u64 start, u32 len)
|
||||
{
|
||||
if (!btrfs_is_subpage(fs_info, folio)) {
|
||||
folio_clear_fixup_pending(folio);
|
||||
return;
|
||||
}
|
||||
btrfs_subpage_clear_fixup(fs_info, folio, start, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlike set/clear which is dependent on each page status, for test all bits
|
||||
* are tested in the same way.
|
||||
@@ -480,6 +685,7 @@ bool btrfs_subpage_test_##name(const struct btrfs_fs_info *fs_info, \
|
||||
IMPLEMENT_BTRFS_SUBPAGE_TEST_OP(uptodate);
|
||||
IMPLEMENT_BTRFS_SUBPAGE_TEST_OP(dirty);
|
||||
IMPLEMENT_BTRFS_SUBPAGE_TEST_OP(writeback);
|
||||
IMPLEMENT_BTRFS_SUBPAGE_TEST_OP(fixup);
|
||||
|
||||
/*
|
||||
* Note that, in selftests (extent-io-tests), we can have empty fs_info passed
|
||||
@@ -571,8 +777,8 @@ bool btrfs_meta_folio_test_##name(struct folio *folio, const struct extent_buffe
|
||||
}
|
||||
IMPLEMENT_BTRFS_PAGE_OPS(uptodate, folio_mark_uptodate, folio_clear_uptodate,
|
||||
folio_test_uptodate);
|
||||
IMPLEMENT_BTRFS_PAGE_OPS(dirty, folio_mark_dirty, folio_clear_dirty_for_io,
|
||||
folio_test_dirty);
|
||||
IMPLEMENT_BTRFS_PAGE_OPS(dirty, btrfs_folio_mark_dirty_reserved,
|
||||
folio_clear_dirty_for_io, folio_test_dirty);
|
||||
IMPLEMENT_BTRFS_PAGE_OPS(writeback, folio_start_writeback, folio_end_writeback,
|
||||
folio_test_writeback);
|
||||
|
||||
|
||||
+36
-5
@@ -14,15 +14,15 @@ struct folio;
|
||||
/*
|
||||
* Extra info for subpage bitmap.
|
||||
*
|
||||
* For subpage we pack all uptodate/dirty/writeback bitmaps into
|
||||
* For subpage we pack all uptodate/dirty/writeback/fixup bitmaps into
|
||||
* one larger bitmap.
|
||||
*
|
||||
* This structure records how they are organized in the bitmap:
|
||||
*
|
||||
* /- uptodate /- dirty /- writeback
|
||||
* | | |
|
||||
* v v v
|
||||
* |u|u|u|u|........|u|u|d|d|.......|d|d|w|w|.......|w|w|
|
||||
* /- uptodate /- dirty /- writeback /- fixup
|
||||
* | | | |
|
||||
* v v v v
|
||||
* |u|u|u|u|........|u|u|d|d|.......|d|d|w|w|.....|w|w|f|f|.....|f|f|
|
||||
* |< sectors_per_page >|
|
||||
*
|
||||
* Unlike regular macro-like enums, here we do not go upper-case names, as
|
||||
@@ -40,6 +40,14 @@ enum {
|
||||
*/
|
||||
btrfs_bitmap_nr_writeback,
|
||||
|
||||
/*
|
||||
* Blocks dirtied by the dirty_folio callback instead of a reserving
|
||||
* write path (e.g. set_page_dirty_lock() on a GUP pin). They have
|
||||
* no space reservation and need the writepage fixup before they can
|
||||
* be submitted.
|
||||
*/
|
||||
btrfs_bitmap_nr_fixup,
|
||||
|
||||
btrfs_bitmap_nr_max
|
||||
};
|
||||
|
||||
@@ -165,6 +173,29 @@ DECLARE_BTRFS_SUBPAGE_OPS(uptodate);
|
||||
DECLARE_BTRFS_SUBPAGE_OPS(dirty);
|
||||
DECLARE_BTRFS_SUBPAGE_OPS(writeback);
|
||||
|
||||
/*
|
||||
* Fixup bit helpers.
|
||||
*
|
||||
* The fixup bit is data-only and has no plain set helper (setting happens
|
||||
* together with dirtying in btrfs_subpage_set_fixup_dirty()), so it does not
|
||||
* go through DECLARE_BTRFS_SUBPAGE_OPS(). For single-block folios the
|
||||
* folio_*_fixup_pending() flag takes the place of the bitmap.
|
||||
*/
|
||||
void btrfs_subpage_clear_fixup(const struct btrfs_fs_info *fs_info,
|
||||
struct folio *folio, u64 start, u32 len);
|
||||
bool btrfs_subpage_test_fixup(const struct btrfs_fs_info *fs_info,
|
||||
struct folio *folio, u64 start, u32 len);
|
||||
bool btrfs_folio_test_fixup(const struct btrfs_fs_info *fs_info,
|
||||
struct folio *folio, u64 start, u32 len);
|
||||
void btrfs_folio_set_fixup_dirty(const struct btrfs_fs_info *fs_info,
|
||||
struct folio *folio, u64 start, u32 len);
|
||||
/* For a block that just got its space reserved; it stays dirty. */
|
||||
void btrfs_folio_clear_fixup(const struct btrfs_fs_info *fs_info,
|
||||
struct folio *folio, u64 start, u32 len);
|
||||
/* For callers discarding the data; clears the dirty bits too. */
|
||||
void btrfs_folio_clear_fixup_dirty(const struct btrfs_fs_info *fs_info,
|
||||
struct folio *folio, u64 start, u32 len);
|
||||
|
||||
/*
|
||||
* Helper for error cleanup, where a folio will have its dirty flag cleared,
|
||||
* with writeback started and finished.
|
||||
|
||||
+1
-1
@@ -534,7 +534,7 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg)
|
||||
return -EFAULT;
|
||||
policy.version = version;
|
||||
|
||||
if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
|
||||
if (!inode_owner_or_capable(file_mnt_idmap(filp), inode))
|
||||
return -EACCES;
|
||||
|
||||
ret = mnt_want_write_file(filp);
|
||||
|
||||
+19
-2
@@ -13,6 +13,7 @@
|
||||
|
||||
struct bio_set iomap_ioend_bioset;
|
||||
EXPORT_SYMBOL_GPL(iomap_ioend_bioset);
|
||||
static struct bio_set iomap_ioend_split_bioset;
|
||||
|
||||
struct iomap_ioend *iomap_init_ioend(struct inode *inode,
|
||||
struct bio *bio, loff_t file_offset, u16 ioend_flags)
|
||||
@@ -488,7 +489,8 @@ struct iomap_ioend *iomap_split_ioend(struct iomap_ioend *ioend,
|
||||
sector_offset = ALIGN_DOWN(sector_offset << SECTOR_SHIFT,
|
||||
i_blocksize(ioend->io_inode)) >> SECTOR_SHIFT;
|
||||
|
||||
split = bio_split(bio, sector_offset, GFP_NOFS, &iomap_ioend_bioset);
|
||||
split = bio_split(bio, sector_offset, GFP_NOFS,
|
||||
&iomap_ioend_split_bioset);
|
||||
if (IS_ERR(split))
|
||||
return ERR_CAST(split);
|
||||
split->bi_private = bio->bi_private;
|
||||
@@ -511,8 +513,23 @@ EXPORT_SYMBOL_GPL(iomap_split_ioend);
|
||||
|
||||
static int __init iomap_ioend_init(void)
|
||||
{
|
||||
return bioset_init(&iomap_ioend_bioset, 4 * (PAGE_SIZE / SECTOR_SIZE),
|
||||
const unsigned int nr_mempool_entries = 4 * (PAGE_SIZE / SECTOR_SIZE);
|
||||
int error;
|
||||
|
||||
error = bioset_init(&iomap_ioend_bioset, nr_mempool_entries,
|
||||
offsetof(struct iomap_ioend, io_bio),
|
||||
BIOSET_NEED_BVECS);
|
||||
if (error)
|
||||
return error;
|
||||
error = bioset_init(&iomap_ioend_split_bioset, nr_mempool_entries,
|
||||
offsetof(struct iomap_ioend, io_bio),
|
||||
BIOSET_NEED_BVECS);
|
||||
if (error)
|
||||
goto out_exit_ioend_bioset;
|
||||
return 0;
|
||||
|
||||
out_exit_ioend_bioset:
|
||||
bioset_exit(&iomap_ioend_bioset);
|
||||
return error;
|
||||
}
|
||||
fs_initcall(iomap_ioend_init);
|
||||
|
||||
@@ -102,8 +102,10 @@ static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq,
|
||||
|
||||
added = rolling_buffer_load_from_ra(&rreq->buffer, ractl,
|
||||
&put_batch);
|
||||
if (added < 0)
|
||||
if (added < 0) {
|
||||
folio_batch_release(&put_batch);
|
||||
return added;
|
||||
}
|
||||
rreq->submitted += added;
|
||||
}
|
||||
folio_batch_release(&put_batch);
|
||||
@@ -359,7 +361,7 @@ void netfs_readahead(struct readahead_control *ractl)
|
||||
netfs_rreq_expand(rreq, ractl);
|
||||
|
||||
rreq->submitted = rreq->start;
|
||||
if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST) < 0)
|
||||
if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST, rreq->gfp) < 0)
|
||||
goto cleanup_free;
|
||||
netfs_read_to_pagecache(rreq, ractl);
|
||||
|
||||
@@ -378,10 +380,10 @@ static int netfs_create_singular_buffer(struct netfs_io_request *rreq, struct fo
|
||||
{
|
||||
ssize_t added;
|
||||
|
||||
if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST) < 0)
|
||||
if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST, rreq->gfp) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
added = rolling_buffer_append(&rreq->buffer, folio, rollbuf_flags);
|
||||
added = rolling_buffer_append(&rreq->buffer, folio, rollbuf_flags, rreq->gfp);
|
||||
if (added < 0)
|
||||
return added;
|
||||
rreq->submitted = rreq->start + added;
|
||||
|
||||
@@ -43,6 +43,7 @@ extern struct list_head netfs_io_requests;
|
||||
extern spinlock_t netfs_proc_lock;
|
||||
extern mempool_t netfs_request_pool;
|
||||
extern mempool_t netfs_subrequest_pool;
|
||||
extern mempool_t netfs_folioq_pool;
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static inline void netfs_proc_add_rreq(struct netfs_io_request *rreq)
|
||||
|
||||
@@ -28,6 +28,7 @@ static struct kmem_cache *netfs_request_slab;
|
||||
static struct kmem_cache *netfs_subrequest_slab;
|
||||
mempool_t netfs_request_pool;
|
||||
mempool_t netfs_subrequest_pool;
|
||||
mempool_t netfs_folioq_pool;
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
LIST_HEAD(netfs_io_requests);
|
||||
@@ -108,6 +109,9 @@ static int __init netfs_init(void)
|
||||
{
|
||||
int ret = -ENOMEM;
|
||||
|
||||
if (mempool_init_kmalloc_pool(&netfs_folioq_pool, 100, sizeof(struct folio_queue)) < 0)
|
||||
goto error_folioq_pool;
|
||||
|
||||
netfs_request_slab = kmem_cache_create("netfs_request",
|
||||
sizeof(struct netfs_io_request), 0,
|
||||
SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT,
|
||||
@@ -160,6 +164,8 @@ error_subreq:
|
||||
error_reqpool:
|
||||
kmem_cache_destroy(netfs_request_slab);
|
||||
error_req:
|
||||
mempool_exit(&netfs_folioq_pool);
|
||||
error_folioq_pool:
|
||||
return ret;
|
||||
}
|
||||
fs_initcall(netfs_init);
|
||||
@@ -172,5 +178,6 @@ static void __exit netfs_exit(void)
|
||||
kmem_cache_destroy(netfs_subrequest_slab);
|
||||
mempool_exit(&netfs_request_pool);
|
||||
kmem_cache_destroy(netfs_request_slab);
|
||||
mempool_exit(&netfs_folioq_pool);
|
||||
}
|
||||
module_exit(netfs_exit);
|
||||
|
||||
+17
-13
@@ -7,7 +7,6 @@
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mempool.h>
|
||||
#include <linux/delay.h>
|
||||
#include "internal.h"
|
||||
|
||||
static void netfs_free_request(struct work_struct *work);
|
||||
@@ -26,17 +25,23 @@ struct netfs_io_request *netfs_alloc_request(struct address_space *mapping,
|
||||
struct netfs_io_request *rreq;
|
||||
mempool_t *mempool = ctx->ops->request_pool ?: &netfs_request_pool;
|
||||
struct kmem_cache *cache = mempool->pool_data;
|
||||
gfp_t gfp = GFP_KERNEL;
|
||||
int ret;
|
||||
|
||||
for (;;) {
|
||||
rreq = mempool_alloc(mempool, GFP_KERNEL);
|
||||
if (rreq)
|
||||
break;
|
||||
msleep(10);
|
||||
/* Writeback is part of memory reclaim and must not fail due to ENOMEM. */
|
||||
if (origin == NETFS_WRITEBACK || origin == NETFS_WRITEBACK_SINGLE) {
|
||||
gfp = GFP_NOFS; /* Allows use of mempools. */
|
||||
|
||||
rreq = mempool_alloc(mempool, gfp);
|
||||
} else {
|
||||
rreq = mempool->alloc(gfp, mempool->pool_data);
|
||||
if (!rreq)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
memset(rreq, 0, kmem_cache_size(cache));
|
||||
INIT_WORK(&rreq->cleanup_work, netfs_free_request);
|
||||
rreq->gfp = gfp;
|
||||
rreq->start = start;
|
||||
rreq->len = len;
|
||||
rreq->origin = origin;
|
||||
@@ -200,13 +205,12 @@ struct netfs_io_subrequest *netfs_alloc_subrequest(struct netfs_io_request *rreq
|
||||
mempool_t *mempool = rreq->netfs_ops->subrequest_pool ?: &netfs_subrequest_pool;
|
||||
struct kmem_cache *cache = mempool->pool_data;
|
||||
|
||||
for (;;) {
|
||||
subreq = mempool_alloc(rreq->netfs_ops->subrequest_pool ?: &netfs_subrequest_pool,
|
||||
GFP_KERNEL);
|
||||
if (subreq)
|
||||
break;
|
||||
msleep(10);
|
||||
}
|
||||
if (rreq->gfp == GFP_KERNEL)
|
||||
subreq = mempool->alloc(rreq->gfp, mempool->pool_data);
|
||||
else
|
||||
subreq = mempool_alloc(mempool, rreq->gfp);
|
||||
if (!subreq)
|
||||
return NULL;
|
||||
|
||||
memset(subreq, 0, kmem_cache_size(cache));
|
||||
INIT_WORK(&subreq->work, NULL);
|
||||
|
||||
@@ -53,7 +53,8 @@ static void netfs_pgpriv2_copy_folio(struct netfs_io_request *creq, struct folio
|
||||
trace_netfs_folio(folio, netfs_folio_trace_store_copy);
|
||||
|
||||
/* Attach the folio to the rolling buffer. */
|
||||
if (rolling_buffer_append(&creq->buffer, folio, 0) < 0) {
|
||||
if (rolling_buffer_append(&creq->buffer, folio, 0, creq->gfp) < 0) {
|
||||
folio_end_private_2(folio);
|
||||
clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &creq->flags);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/mempool.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/rolling_buffer.h>
|
||||
#include <linux/slab.h>
|
||||
@@ -27,7 +28,10 @@ struct folio_queue *netfs_folioq_alloc(unsigned int rreq_id, gfp_t gfp,
|
||||
{
|
||||
struct folio_queue *fq;
|
||||
|
||||
fq = kmalloc_obj(*fq, gfp);
|
||||
if (gfp == GFP_KERNEL)
|
||||
fq = netfs_folioq_pool.alloc(gfp, netfs_folioq_pool.pool_data);
|
||||
else
|
||||
fq = mempool_alloc(&netfs_folioq_pool, gfp);
|
||||
if (fq) {
|
||||
netfs_stat(&netfs_n_folioq);
|
||||
folioq_init(fq, rreq_id);
|
||||
@@ -50,7 +54,7 @@ void netfs_folioq_free(struct folio_queue *folioq,
|
||||
{
|
||||
trace_netfs_folioq(folioq, trace);
|
||||
netfs_stat_d(&netfs_n_folioq);
|
||||
kfree(folioq);
|
||||
mempool_free(folioq, &netfs_folioq_pool);
|
||||
}
|
||||
EXPORT_SYMBOL(netfs_folioq_free);
|
||||
|
||||
@@ -60,11 +64,11 @@ EXPORT_SYMBOL(netfs_folioq_free);
|
||||
* consumer.
|
||||
*/
|
||||
int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id,
|
||||
unsigned int direction)
|
||||
unsigned int direction, gfp_t gfp)
|
||||
{
|
||||
struct folio_queue *fq;
|
||||
|
||||
fq = netfs_folioq_alloc(rreq_id, GFP_NOFS, netfs_trace_folioq_rollbuf_init);
|
||||
fq = netfs_folioq_alloc(rreq_id, gfp, netfs_trace_folioq_rollbuf_init);
|
||||
if (!fq)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -77,14 +81,14 @@ int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id,
|
||||
/*
|
||||
* Add another folio_queue to a rolling buffer if there's no space left.
|
||||
*/
|
||||
int rolling_buffer_make_space(struct rolling_buffer *roll)
|
||||
int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp)
|
||||
{
|
||||
struct folio_queue *fq, *head = roll->head;
|
||||
|
||||
if (!folioq_full(head))
|
||||
return 0;
|
||||
|
||||
fq = netfs_folioq_alloc(head->rreq_id, GFP_NOFS, netfs_trace_folioq_make_space);
|
||||
fq = netfs_folioq_alloc(head->rreq_id, gfp, netfs_trace_folioq_make_space);
|
||||
if (!fq)
|
||||
return -ENOMEM;
|
||||
fq->prev = head;
|
||||
@@ -122,7 +126,7 @@ ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll,
|
||||
int nr, ix, to;
|
||||
ssize_t size = 0;
|
||||
|
||||
if (rolling_buffer_make_space(roll) < 0)
|
||||
if (rolling_buffer_make_space(roll, GFP_KERNEL) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
fq = roll->head;
|
||||
@@ -153,12 +157,12 @@ ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll,
|
||||
* Append a folio to the rolling buffer.
|
||||
*/
|
||||
ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio,
|
||||
unsigned int flags)
|
||||
unsigned int flags, gfp_t gfp)
|
||||
{
|
||||
ssize_t size = folio_size(folio);
|
||||
int slot;
|
||||
|
||||
if (rolling_buffer_make_space(roll) < 0)
|
||||
if (rolling_buffer_make_space(roll, gfp) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
slot = folioq_append(roll->head, folio);
|
||||
|
||||
+10
-5
@@ -108,7 +108,7 @@ struct netfs_io_request *netfs_create_write_req(struct address_space *mapping,
|
||||
ictx = netfs_inode(wreq->inode);
|
||||
if (is_cacheable)
|
||||
fscache_begin_write_operation(&wreq->cache_resources, netfs_i_cookie(ictx));
|
||||
if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE) < 0)
|
||||
if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE, wreq->gfp) < 0)
|
||||
goto nomem;
|
||||
|
||||
wreq->cleaned_to = wreq->start;
|
||||
@@ -167,7 +167,7 @@ void netfs_prepare_write(struct netfs_io_request *wreq,
|
||||
*/
|
||||
if (iov_iter_is_folioq(wreq_iter) &&
|
||||
wreq_iter->folioq_slot >= folioq_nr_slots(wreq_iter->folioq))
|
||||
rolling_buffer_make_space(&wreq->buffer);
|
||||
rolling_buffer_make_space(&wreq->buffer, wreq->gfp);
|
||||
|
||||
subreq = netfs_alloc_subrequest(wreq);
|
||||
subreq->source = stream->source;
|
||||
@@ -334,7 +334,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq,
|
||||
|
||||
_enter("");
|
||||
|
||||
if (rolling_buffer_make_space(&wreq->buffer) < 0)
|
||||
if (rolling_buffer_make_space(&wreq->buffer, wreq->gfp) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
/* netfs_perform_write() may shift i_size around the page or from out
|
||||
@@ -436,7 +436,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq,
|
||||
}
|
||||
|
||||
/* Attach the folio to the rolling buffer. */
|
||||
rolling_buffer_append(&wreq->buffer, folio, 0);
|
||||
rolling_buffer_append(&wreq->buffer, folio, 0, wreq->gfp);
|
||||
|
||||
/* Move the submission point forward to allow for write-streaming data
|
||||
* not starting at the front of the page. We don't do write-streaming
|
||||
@@ -720,6 +720,7 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq,
|
||||
size_t iter_off = 0;
|
||||
size_t fsize = folio_size(folio), flen;
|
||||
loff_t fpos = folio_pos(folio);
|
||||
ssize_t ret;
|
||||
bool to_eof = false;
|
||||
bool no_debug = false;
|
||||
|
||||
@@ -748,7 +749,11 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq,
|
||||
|
||||
/* Attach the folio to the rolling buffer. */
|
||||
folio_get(folio);
|
||||
rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK);
|
||||
ret = rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK, wreq->gfp);
|
||||
if (ret < 0) {
|
||||
folio_put(folio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Move the submission point forward to allow for write-streaming data
|
||||
* not starting at the front of the page. We don't do write-streaming
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user