diff options
| author | Mark Brown <broonie@kernel.org> | 2026-09-07 12:11:53 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-09-07 12:11:53 +0100 |
| commit | bb1a0fd5f73eae572a67307fabd268fe18e786af (patch) | |
| tree | f03003d10a28ee6fd861aaf0a73e44dc97c7011e | |
| parent | df2908090cda368b01ff43709f51890076c56157 (diff) | |
| parent | e14d4302cbd0de773960bec33c2281508c8d8855 (diff) | |
| download | linux-next-bb1a0fd5f73eae572a67307fabd268fe18e786af.tar.gz linux-next-bb1a0fd5f73eae572a67307fabd268fe18e786af.zip | |
Merge branch 'vfs.fixes' of https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
35 files changed, 507 insertions, 215 deletions
diff --git a/fs/adfs/super.c b/fs/adfs/super.c index a4cd0a5159dd..888aa81a6b39 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -92,10 +92,7 @@ static int adfs_checkdiscrecord(struct adfs_discrecord *dr) static void adfs_put_super(struct super_block *sb) { - struct adfs_sb_info *asb = ADFS_SB(sb); - adfs_free_map(sb); - kfree_rcu(asb, rcu); } static int adfs_show_options(struct seq_file *seq, struct dentry *root) @@ -365,7 +362,7 @@ static int adfs_fill_super(struct super_block *sb, struct fs_context *fc) ret = -EINVAL; } if (ret) - goto error; + return ret; /* set up enough so that we can read an inode */ sb->s_op = &adfs_sops; @@ -406,15 +403,9 @@ static int adfs_fill_super(struct super_block *sb, struct fs_context *fc) if (!sb->s_root) { adfs_free_map(sb); adfs_error(sb, "get root inode failed\n"); - ret = -EIO; - goto error; + return -EIO; } return 0; - -error: - sb->s_fs_info = NULL; - kfree(asb); - return ret; } static int adfs_get_tree(struct fs_context *fc) @@ -465,10 +456,19 @@ static int adfs_init_fs_context(struct fs_context *fc) return 0; } +static void adfs_kill_sb(struct super_block *sb) +{ + struct adfs_sb_info *asb = ADFS_SB(sb); + + kill_block_super(sb); + + kfree_rcu(asb, rcu); +} + static struct file_system_type adfs_fs_type = { .owner = THIS_MODULE, .name = "adfs", - .kill_sb = kill_block_super, + .kill_sb = adfs_kill_sb, .fs_flags = FS_REQUIRES_DEV, .init_fs_context = adfs_init_fs_context, .parameters = adfs_param_spec, diff --git a/fs/afs/addr_list.c b/fs/afs/addr_list.c index 63bf096b721a..73195d76b481 100644 --- a/fs/afs/addr_list.c +++ b/fs/afs/addr_list.c @@ -394,8 +394,11 @@ void afs_set_peer_appdata(struct afs_server *server, struct rxrpc_peer *pn = new_alist->addrs[n].peer; struct rxrpc_peer *po = old_alist->addrs[o].peer; - if (pn == po) + if (pn == po) { + n++; + o++; continue; + } if (pn < po) { rxrpc_kernel_set_peer_data(pn, data); n++; diff --git a/fs/afs/dir_edit.c b/fs/afs/dir_edit.c index 3ead36a07048..c31303059444 100644 --- a/fs/afs/dir_edit.c +++ b/fs/afs/dir_edit.c @@ -442,7 +442,7 @@ void afs_edit_dir_remove(struct afs_vnode *vnode, /* Check and clear the entry. */ de = &block->dirents[slot]; if (de->u.valid != 1) - goto error_unmap; + goto error; trace_afs_edit_dir(vnode, why, afs_edit_dir_delete, b, slot, ntohl(de->u.vnode), ntohl(de->u.unique), @@ -458,7 +458,6 @@ void afs_edit_dir_remove(struct afs_vnode *vnode, /* Clear the constituent entries. */ next = de->u.hash_next; memset(de, 0, sizeof(*de) * iter.nr_slots); - kunmap_local(block); /* Adjust the hash chain: if iter->prev_entry is 0, the hashtable head * index is previous; otherwise it's slot number of the previous entry. @@ -485,7 +484,6 @@ void afs_edit_dir_remove(struct afs_vnode *vnode, pde = &pblock->dirents[ps]; prev_next = pde->u.hash_next; if (prev_next != htons(entry)) { - kunmap_local(pblock); pr_warn("%llx:%llx:%x: not prev in chain b=%x p=%x,%x e=%x %*s", vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique, iter.bucket, iter.prev_entry, prev_next, entry, @@ -493,7 +491,6 @@ void afs_edit_dir_remove(struct afs_vnode *vnode, goto error; } pde->u.hash_next = next; - kunmap_local(pblock); } netfs_single_mark_inode_dirty(&vnode->netfs.inode); @@ -503,18 +500,16 @@ void afs_edit_dir_remove(struct afs_vnode *vnode, _debug("Remove %s from %u[%u]", name->name, b, slot); out_unmap: + afs_dir_end_iter(&iter); kunmap_local(meta); _leave(""); return; already_invalidated: - kunmap_local(block); trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_inval, 0, 0, 0, 0, name->name); goto out_unmap; -error_unmap: - kunmap_local(block); error: trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_error, 0, 0, 0, 0, name->name); diff --git a/fs/afs/dir_search.c b/fs/afs/dir_search.c index 104411c0692f..11ebdfffcb1d 100644 --- a/fs/afs/dir_search.c +++ b/fs/afs/dir_search.c @@ -75,10 +75,7 @@ union afs_xdr_dir_block *afs_dir_find_block(struct afs_dir_iter *iter, size_t bl _enter("%zx,%d", block, slot); - if (iter->block) { - kunmap_local(iter->block); - iter->block = NULL; - } + afs_dir_end_iter(iter); if (dvnode->directory_size < blend) goto fail; @@ -173,12 +170,8 @@ int afs_dir_search_bucket(struct afs_dir_iter *iter, const struct qstr *name, ret = -ENOENT; found: - if (iter->block) { - kunmap_local(iter->block); - iter->block = NULL; - } - bad: + afs_dir_end_iter(iter); if (ret == -ESTALE) afs_invalidate_dir(iter->dvnode, afs_dir_invalid_iter_stale); _leave(" = %d", ret); diff --git a/fs/afs/fs_probe.c b/fs/afs/fs_probe.c index a91ad1938d07..8c62334dbfe7 100644 --- a/fs/afs/fs_probe.c +++ b/fs/afs/fs_probe.c @@ -258,6 +258,7 @@ int afs_fs_probe_fileserver(struct afs_net *net, struct afs_server *server, lockdep_is_held(&server->fs_lock)); if (old) { estate->responsive_set = old->responsive_set; + old_alist = old->addresses; if (!new_alist) new_alist = old->addresses; } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 290873bac89b..330654ed16ec 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -1133,6 +1133,14 @@ int afs_dir_search_bucket(struct afs_dir_iter *iter, const struct qstr *name, int afs_dir_search(struct afs_vnode *dvnode, const struct qstr *name, struct afs_fid *_fid, afs_dataversion_t *_dir_version); +static inline void afs_dir_end_iter(struct afs_dir_iter *iter) +{ + if (iter->block) { + kunmap_local(iter->block); + iter->block = NULL; + } +} + /* * dir_silly.c */ diff --git a/fs/afs/server.c b/fs/afs/server.c index 0fe162ea2a36..189138bd6d71 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -242,7 +242,6 @@ struct afs_server *afs_lookup_server(struct afs_cell *cell, struct key *key, out: afs_put_addrlist(alist, afs_alist_trace_put_server_create); if (candidate) { - kfree(rcu_access_pointer(server->endpoint_state)); kfree(candidate); afs_dec_servers_outstanding(cell->net); } diff --git a/fs/cachefiles/xattr.c b/fs/cachefiles/xattr.c index f8ae78b3f7b6..c70bf67e52b0 100644 --- a/fs/cachefiles/xattr.c +++ b/fs/cachefiles/xattr.c @@ -13,6 +13,7 @@ #include <linux/quotaops.h> #include <linux/xattr.h> #include <linux/slab.h> +#include <linux/unaligned.h> #include "internal.h" #define CACHEFILES_COOKIE_TYPE_DATA 1 @@ -50,7 +51,7 @@ int cachefiles_set_object_xattr(struct cachefiles_object *object) _enter("%x,#%d", object->debug_id, len); - buf = kmalloc(sizeof(struct cachefiles_xattr) + len, GFP_KERNEL); + buf = kmalloc(sizeof(struct cachefiles_xattr) + max(len, sizeof(__be64)), GFP_KERNEL); if (!buf) return -ENOMEM; @@ -60,6 +61,7 @@ int cachefiles_set_object_xattr(struct cachefiles_object *object) buf->content = object->content_info; if (test_bit(FSCACHE_COOKIE_LOCAL_WRITE, &object->cookie->flags)) buf->content = CACHEFILES_CONTENT_DIRTY; + put_unaligned_be64(0, (__be64 *)buf->data); if (len > 0) memcpy(buf->data, fscache_get_aux(object->cookie), len); @@ -77,8 +79,7 @@ int cachefiles_set_object_xattr(struct cachefiles_object *object) trace_cachefiles_vfs_error(object, file_inode(file), ret, cachefiles_trace_setxattr_error); trace_cachefiles_coherency(object, file_inode(file)->i_ino, - be64_to_cpup((__be64 *)buf->data), - buf->content, + buf->data, buf->content, cachefiles_coherency_set_fail); if (ret != -ENOMEM) cachefiles_io_error_obj( @@ -86,8 +87,7 @@ int cachefiles_set_object_xattr(struct cachefiles_object *object) "Failed to set xattr with error %d", ret); } else { trace_cachefiles_coherency(object, file_inode(file)->i_ino, - be64_to_cpup((__be64 *)buf->data), - buf->content, + buf->data, buf->content, cachefiles_coherency_set_ok); } @@ -110,9 +110,10 @@ int cachefiles_check_auxdata(struct cachefiles_object *object, struct file *file int ret = -ESTALE; tlen = sizeof(struct cachefiles_xattr) + len; - buf = kmalloc(tlen, GFP_KERNEL); + buf = kmalloc(sizeof(struct cachefiles_xattr) + max(len, sizeof(__be64)), GFP_KERNEL); if (!buf) return -ENOMEM; + put_unaligned_be64(0, (__be64 *)buf->data); xlen = cachefiles_inject_read_error(); if (xlen == 0) @@ -148,8 +149,7 @@ int cachefiles_check_auxdata(struct cachefiles_object *object, struct file *file out: trace_cachefiles_coherency(object, file_inode(file)->i_ino, - be64_to_cpup((__be64 *)buf->data), - buf->content, why); + buf->data, buf->content, why); kfree(buf); return ret; } diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index bd4b778df9eb..26f0f9714f03 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -6456,9 +6456,10 @@ int ext4_chunk_trans_blocks(struct inode *inode, int nrblocks) int ext4_mark_iloc_dirty(handle_t *handle, struct inode *inode, struct ext4_iloc *iloc) { + struct super_block *sb = inode->i_sb; int err = 0; - err = ext4_emergency_state(inode->i_sb); + err = ext4_emergency_state(sb); if (unlikely(err)) { put_bh(iloc->bh); return err; @@ -6473,9 +6474,13 @@ int ext4_mark_iloc_dirty(handle_t *handle, put_bh(iloc->bh); /* * Mark that there's metadata writeout pending for the inode so that it - * gets properly flushed on fsync(2) and similar. + * gets properly flushed on fsync(2) and similar. We don't bother for + * fastcommit replay as that flushes the whole bdev afterwards anyway. + * It is faster this way and we avoid entering fs writeback paths which + * aren't fully initialized yet. */ - if (!EXT4_SB(inode->i_sb)->s_journal) { + if (!ext4_handle_valid(handle) && + !(EXT4_SB(sb)->s_mount_state & EXT4_FC_REPLAY)) { /* * Inode didn't need to go through dirtying, make sure it is * attached to wb so that writeback can handle it. diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 7fdfa4f27e34..424df70a5c30 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -55,6 +55,42 @@ static void netfs_rreq_expand(struct netfs_io_request *rreq, } /* + * Drop the folio refs acquired from the readahead API. + */ +static void netfs_bulk_drop_ra_refs(struct netfs_io_request *rreq) +{ + struct folio_batch fbatch; + struct folio *folio; + pgoff_t nr_pages = DIV_ROUND_UP(rreq->len, PAGE_SIZE); + pgoff_t first = rreq->start / PAGE_SIZE; + XA_STATE(xas, &rreq->mapping->i_pages, first); + + folio_batch_init(&fbatch); + + rcu_read_lock(); + + xas_for_each(&xas, folio, first + nr_pages - 1) { + if (xas_retry(&xas, folio)) + continue; + + if (!folio_batch_add(&fbatch, folio)) + folio_batch_release(&fbatch); + } + + rcu_read_unlock(); + folio_batch_release(&fbatch); + trace_netfs_rreq(rreq, netfs_rreq_trace_ra_put_ref); + clear_bit_unlock(NETFS_RREQ_NEED_PUT_RA_REFS, &rreq->flags); + wake_up(&rreq->waitq); +} + +static void netfs_maybe_bulk_drop_ra_refs(struct netfs_io_request *rreq) +{ + if (test_bit(NETFS_RREQ_NEED_PUT_RA_REFS, &rreq->flags)) + netfs_bulk_drop_ra_refs(rreq); +} + +/* * Begin an operation, and fetch the stored zero point value from the cookie if * available. */ @@ -74,12 +110,8 @@ static int netfs_begin_cache_read(struct netfs_io_request *rreq, struct netfs_in * * Returns the limited size if successful and -ENOMEM if insufficient memory * available. - * - * [!] NOTE: This must be run in the same thread as ->issue_read() was called - * in as we access the readahead_control struct. */ -static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq, - struct readahead_control *ractl) +static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq) { struct netfs_io_request *rreq = subreq->rreq; size_t rsize = subreq->len; @@ -87,30 +119,6 @@ static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq, if (subreq->source == NETFS_DOWNLOAD_FROM_SERVER) rsize = umin(rsize, rreq->io_streams[0].sreq_max_len); - if (ractl) { - /* If we don't have sufficient folios in the rolling buffer, - * extract a folioq's worth from the readahead region at a time - * into the buffer. Note that this acquires a ref on each page - * that we will need to release later - but we don't want to do - * that until after we've started the I/O. - */ - struct folio_batch put_batch; - - folio_batch_init(&put_batch); - while (rreq->submitted < subreq->start + rsize) { - ssize_t added; - - added = rolling_buffer_load_from_ra(&rreq->buffer, ractl, - &put_batch); - if (added < 0) { - folio_batch_release(&put_batch); - return added; - } - rreq->submitted += added; - } - folio_batch_release(&put_batch); - } - subreq->len = rsize; if (unlikely(rreq->io_streams[0].sreq_max_segs)) { size_t limit = netfs_limit_iter(&rreq->buffer.iter, 0, rsize, @@ -204,16 +212,67 @@ static void netfs_issue_read(struct netfs_io_request *rreq, } /* + * Mark folios that we want to copy to the cache. For filesystems that use + * netfslib fully, we set folio->private to NETFS_FOLIO_COPY_TO_CACHE; + * otherwise we set the deprecated PG_private_2. + */ +static void netfs_mark_copy_to_cache(struct netfs_io_request *rreq, + struct folio_queue **fq, + unsigned int *offset, + int *slot, + size_t len, + bool copy) +{ + while (len > 0) { + struct folio *folio; + size_t fsize, overlap; + + if (!*fq) + break; + if (*slot >= folioq_count(*fq)) { + *fq = (*fq)->next; + *slot = 0; + *offset = 0; + continue; + } + + /* Determine how much the subreq overlaps the folio, if at all. */ + fsize = folioq_folio_size(*fq, *slot); + overlap = min(len, fsize - *offset); + + if (overlap > 0 && copy) { + folio = folioq_folio(*fq, *slot); + if (unlikely(test_bit(NETFS_RREQ_USE_PGPRIV2, &rreq->flags))) { + if (!folio_test_private_2(folio)) + folio_start_private_2(folio); + } else { + if (!folio_get_private(folio)) + folio_attach_private(folio, NETFS_FOLIO_COPY_TO_CACHE); + } + trace_netfs_folio(folio, netfs_folio_trace_mark_copy); + } + + len -= overlap; + *offset += overlap; + if (*offset >= fsize) { + *slot += 1; + *offset = 0; + } + } +} + +/* * Perform a read to the pagecache from a series of sources of different types, * slicing up the region to be read according to available cache blocks and * network rsize. */ -static void netfs_read_to_pagecache(struct netfs_io_request *rreq, - struct readahead_control *ractl) +static void netfs_read_to_pagecache(struct netfs_io_request *rreq) { + struct folio_queue *fq = rreq->buffer.tail; unsigned long long start = rreq->start; + unsigned int offset = 0; ssize_t size = rreq->len; - int ret = 0; + int ret = 0, slot = 0; do { struct netfs_io_subrequest *subreq; @@ -288,7 +347,7 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, break; issue: - slice = netfs_prepare_read_iterator(subreq, ractl); + slice = netfs_prepare_read_iterator(subreq); if (slice < 0) { ret = slice; netfs_cancel_read(subreq, ret); @@ -301,7 +360,15 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); } + if (fq) { + /* See if the cache indicated this should be cached. */ + bool copy = test_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags); + + netfs_mark_copy_to_cache(rreq, &fq, &slot, &offset, slice, copy); + } + netfs_issue_read(rreq, subreq); + netfs_maybe_bulk_drop_ra_refs(rreq); if (test_bit(NETFS_RREQ_PAUSE, &rreq->flags)) netfs_wait_for_paused_read(rreq); @@ -339,7 +406,8 @@ void netfs_readahead(struct readahead_control *ractl) { struct netfs_io_request *rreq; struct netfs_inode *ictx = netfs_inode(ractl->mapping->host); - unsigned long long start = readahead_pos(ractl); + ssize_t added; + uoff_t start = readahead_pos(ractl); size_t size = readahead_length(ractl); int ret; @@ -360,11 +428,24 @@ 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, rreq->gfp) < 0) + /* Load the folios to be read into a bvecq chain. Note that this + * acquires a ref on each folio that we will need to release later - + * but we don't want to do that until after we've started the I/O. + */ + added = rolling_buffer_bulk_load_from_ra(&rreq->buffer, ractl, + rreq->debug_id, rreq->gfp); + if (added < 0) { + ret = added; goto cleanup_free; - netfs_read_to_pagecache(rreq, ractl); + } + __set_bit(NETFS_RREQ_NEED_PUT_RA_REFS, &rreq->flags); + + rreq->submitted = rreq->start + added; + rreq->cleaned_to = rreq->start; + netfs_read_set_unlock_at(rreq); + netfs_read_to_pagecache(rreq); + netfs_maybe_bulk_drop_ra_refs(rreq); return netfs_put_request(rreq, netfs_rreq_trace_put_return); cleanup_free: @@ -387,6 +468,7 @@ static int netfs_create_singular_buffer(struct netfs_io_request *rreq, struct fo if (added < 0) return added; rreq->submitted = rreq->start + added; + rreq->progress_at = added; return 0; } @@ -457,7 +539,7 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) iov_iter_bvec(&rreq->buffer.iter, ITER_DEST, bvec, i, rreq->len); rreq->submitted = rreq->start + flen; - netfs_read_to_pagecache(rreq, NULL); + netfs_read_to_pagecache(rreq); ret = netfs_wait_for_read(rreq); if (ret >= 0) { @@ -532,7 +614,7 @@ int netfs_read_folio(struct file *file, struct folio *folio) if (ret < 0) goto discard; - netfs_read_to_pagecache(rreq, NULL); + netfs_read_to_pagecache(rreq); ret = netfs_wait_for_read(rreq); netfs_put_request(rreq, netfs_rreq_trace_put_return); return ret < 0 ? ret : 0; @@ -689,7 +771,7 @@ retry: if (ret < 0) goto error_put; - netfs_read_to_pagecache(rreq, NULL); + netfs_read_to_pagecache(rreq); ret = netfs_wait_for_read(rreq); netfs_put_request(rreq, netfs_rreq_trace_put_return); if (ret < 0) @@ -754,7 +836,7 @@ int netfs_prefetch_for_write(struct file *file, struct folio *folio, if (ret < 0) goto error_put; - netfs_read_to_pagecache(rreq, NULL); + netfs_read_to_pagecache(rreq); ret = netfs_wait_for_read(rreq); netfs_put_request(rreq, netfs_rreq_trace_put_return); return ret < 0 ? ret : 0; diff --git a/fs/netfs/direct_write.c b/fs/netfs/direct_write.c index c16fbad286a1..2361277416c7 100644 --- a/fs/netfs/direct_write.c +++ b/fs/netfs/direct_write.c @@ -21,7 +21,7 @@ static void netfs_unbuffered_write_done(struct netfs_io_request *wreq) /* Okay, declare that all I/O is complete. */ trace_netfs_rreq(wreq, netfs_rreq_trace_write_done); - if (!wreq->error) + if (wreq->transferred) netfs_update_i_size(ictx, &ictx->inode, wreq->start, wreq->transferred); if (wreq->origin == NETFS_DIO_WRITE && @@ -51,7 +51,7 @@ static void netfs_unbuffered_write_done(struct netfs_io_request *wreq) wreq->iocb->ki_pos += written; if (wreq->iocb->ki_complete) { trace_netfs_rreq(wreq, netfs_rreq_trace_ki_complete); - wreq->iocb->ki_complete(wreq->iocb, wreq->error ?: written); + wreq->iocb->ki_complete(wreq->iocb, written ?: wreq->error); } wreq->iocb = VFS_PTR_POISON; } @@ -95,7 +95,7 @@ static int netfs_unbuffered_write(struct netfs_io_request *wreq) { struct netfs_io_subrequest *subreq = NULL; struct netfs_io_stream *stream = &wreq->io_streams[0]; - int ret; + int ret = 0; _enter("%llx", wreq->len); @@ -110,6 +110,11 @@ static int netfs_unbuffered_write(struct netfs_io_request *wreq) if (!subreq) { netfs_prepare_write(wreq, stream, wreq->start + wreq->transferred); subreq = stream->construct; + if (!subreq) { + wreq->error = -ENOMEM; + ret = -ENOMEM; + break; + } stream->construct = NULL; } @@ -121,8 +126,14 @@ static int netfs_unbuffered_write(struct netfs_io_request *wreq) } iov_iter_truncate(&subreq->io_iter, wreq->len - wreq->transferred); - if (!iov_iter_count(&subreq->io_iter)) + if (!iov_iter_count(&subreq->io_iter)) { + pr_warn("netfs: Unexpected zero-length iterator R=%08x\n", + wreq->debug_id); + __set_bit(NETFS_SREQ_FAILED, &subreq->flags); + netfs_write_subrequest_terminated(subreq, -EIO); + wreq->error = -EIO; break; + } subreq->len = netfs_limit_iter(&subreq->io_iter, 0, stream->sreq_max_len, @@ -139,13 +150,11 @@ static int netfs_unbuffered_write(struct netfs_io_request *wreq) if (test_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags)) { retry = true; } else if (test_bit(NETFS_SREQ_FAILED, &subreq->flags)) { - ret = subreq->error; - wreq->error = ret; + wreq->error = subreq->error; netfs_see_subrequest(subreq, netfs_sreq_trace_see_failed); subreq = NULL; break; } - ret = 0; if (!retry) { netfs_unbuffered_write_collect(wreq, stream, subreq); @@ -288,11 +297,11 @@ ssize_t netfs_unbuffered_write_iter_locked(struct kiocb *iocb, struct iov_iter * ret = -EIOCBQUEUED; } else { ret = netfs_unbuffered_write(wreq); - if (ret < 0) { - _debug("begin = %zd", ret); - } else { + if (wreq->transferred) { iocb->ki_pos += wreq->transferred; - ret = wreq->transferred ?: wreq->error; + ret = wreq->transferred; + } else if (wreq->error) { + ret = wreq->error; } netfs_put_request(wreq, netfs_rreq_trace_put_complete); diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h index 420ee7b26580..c79c8e69d60c 100644 --- a/fs/netfs/internal.h +++ b/fs/netfs/internal.h @@ -79,6 +79,7 @@ ssize_t netfs_wait_for_read(struct netfs_io_request *rreq); ssize_t netfs_wait_for_write(struct netfs_io_request *rreq); void netfs_wait_for_paused_read(struct netfs_io_request *rreq); void netfs_wait_for_paused_write(struct netfs_io_request *rreq); +void netfs_wait_for_put_ra_refs(struct netfs_io_request *rreq); /* * objects.c @@ -109,6 +110,8 @@ static inline void netfs_see_subrequest(struct netfs_io_subrequest *subreq, /* * read_collect.c */ +void netfs_cancel_copy_to_cache(struct netfs_io_request *rreq, struct folio *folio); +void netfs_read_set_unlock_at(struct netfs_io_request *rreq); bool netfs_read_collection(struct netfs_io_request *rreq); void netfs_read_collection_worker(struct work_struct *work); void netfs_cancel_read(struct netfs_io_subrequest *subreq, int error); diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c index 5d554512ed23..f5c1c463f4ff 100644 --- a/fs/netfs/misc.c +++ b/fs/netfs/misc.c @@ -563,3 +563,22 @@ void netfs_wait_for_paused_write(struct netfs_io_request *rreq) { return netfs_wait_for_pause(rreq, netfs_write_collection); } + +/* + * Wait for the readahead-acquired refs to be put. + */ +void netfs_wait_for_put_ra_refs(struct netfs_io_request *rreq) +{ + DEFINE_WAIT(myself); + + for (;;) { + trace_netfs_rreq(rreq, netfs_rreq_trace_wait_put_ra_refs); + prepare_to_wait(&rreq->waitq, &myself, TASK_UNINTERRUPTIBLE); + if (!test_bit(NETFS_RREQ_NEED_PUT_RA_REFS, &rreq->flags)) + break; + schedule(); + } + + trace_netfs_rreq(rreq, netfs_rreq_trace_waited_put_ra_refs); + finish_wait(&rreq->waitq, &myself); +} diff --git a/fs/netfs/objects.c b/fs/netfs/objects.c index 01461a74642d..7f6a3e912602 100644 --- a/fs/netfs/objects.c +++ b/fs/netfs/objects.c @@ -41,24 +41,32 @@ struct netfs_io_request *netfs_alloc_request(struct address_space *mapping, 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; - rreq->netfs_ops = ctx->ops; - rreq->mapping = mapping; - rreq->inode = inode; - rreq->i_size = i_size_read(inode); - rreq->debug_id = atomic_inc_return(&debug_ids); - rreq->wsize = INT_MAX; + rreq->gfp = gfp; + rreq->start = start; + rreq->collected_to = start; + rreq->cleaned_to = start; + rreq->len = len; + rreq->progress_at = 0; + rreq->origin = origin; + rreq->netfs_ops = ctx->ops; + rreq->mapping = mapping; + rreq->inode = inode; + rreq->i_size = i_size_read(inode); + rreq->debug_id = atomic_inc_return(&debug_ids); + rreq->wsize = INT_MAX; rreq->io_streams[0].sreq_max_len = ULONG_MAX; rreq->io_streams[0].sreq_max_segs = 0; spin_lock_init(&rreq->lock); - INIT_LIST_HEAD(&rreq->io_streams[0].subrequests); - INIT_LIST_HEAD(&rreq->io_streams[1].subrequests); init_waitqueue_head(&rreq->waitq); refcount_set(&rreq->ref, 2); + for (int s = 0; s < NR_IO_STREAMS; s++) { + struct netfs_io_stream *stream = &rreq->io_streams[s]; + + INIT_LIST_HEAD(&stream->subrequests); + stream->collected_to = rreq->start; + } + if (origin == NETFS_READAHEAD || origin == NETFS_READPAGE || origin == NETFS_READ_GAPS || diff --git a/fs/netfs/read_collect.c b/fs/netfs/read_collect.c index 23660a590124..5cf22087d243 100644 --- a/fs/netfs/read_collect.c +++ b/fs/netfs/read_collect.c @@ -19,7 +19,6 @@ #define MADE_PROGRESS 0x04 /* Made progress cleaning up a stream or the folio set */ #define BUFFERED 0x08 /* The pagecache needs cleaning up */ #define NEED_RETRY 0x10 /* A front op requests retrying */ -#define COPY_TO_CACHE 0x40 /* Need to copy subrequest to cache */ #define ABANDON_SREQ 0x80 /* Need to abandon untransferred part of subrequest */ /* @@ -35,6 +34,30 @@ static void netfs_clear_unread(struct netfs_io_subrequest *subreq) } /* + * Cancel the copy-to-cache mark on a folio. + */ +void netfs_cancel_copy_to_cache(struct netfs_io_request *rreq, struct folio *folio) +{ + if (!test_bit(NETFS_RREQ_USE_PGPRIV2, &rreq->flags)) { + if (folio_get_private(folio) == NETFS_FOLIO_COPY_TO_CACHE) { + folio_detach_private(folio); + trace_netfs_folio(folio, netfs_folio_trace_cancel_copy); + } else if (netfs_folio_group(folio) == NETFS_FOLIO_COPY_TO_CACHE) { + struct netfs_folio *finfo = netfs_folio_info(folio); + + finfo->netfs_group = NULL; + trace_netfs_folio(folio, netfs_folio_trace_cancel_copy); + } + } else { + // TODO: Use of PG_private_2 is deprecated. + if (folio_test_private_2(folio)) { + folio_end_private_2(folio); + trace_netfs_folio(folio, netfs_folio_trace_cancel_copy); + } + } +} + +/* * Flush, mark and unlock a folio that's now completely read. If we want to * cache the folio, we set the group to NETFS_FOLIO_COPY_TO_CACHE, mark it * dirty and let writeback handle it. @@ -48,37 +71,37 @@ static void netfs_unlock_read_folio(struct netfs_io_request *rreq, if (unlikely(folio_pos(folio) < rreq->abandon_to)) { trace_netfs_folio(folio, netfs_folio_trace_abandon); + netfs_cancel_copy_to_cache(rreq, folio); goto just_unlock; } flush_dcache_folio(folio); folio_mark_uptodate(folio); - if (!test_bit(NETFS_RREQ_USE_PGPRIV2, &rreq->flags)) { - finfo = netfs_folio_info(folio); - if (finfo) { - trace_netfs_folio(folio, netfs_folio_trace_filled_gaps); - if (finfo->netfs_group) - folio_change_private(folio, finfo->netfs_group); - else - folio_detach_private(folio); - kfree(finfo); - } + if (unlikely(test_bit(NETFS_RREQ_CANCEL_CACHING, &rreq->flags))) + netfs_cancel_copy_to_cache(rreq, folio); - if (test_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &rreq->flags)) { - if (!WARN_ON_ONCE(folio_get_private(folio) != NULL)) { - trace_netfs_folio(folio, netfs_folio_trace_copy_to_cache); - folio_attach_private(folio, NETFS_FOLIO_COPY_TO_CACHE); - folio_mark_dirty(folio); - } + if (!test_bit(NETFS_RREQ_USE_PGPRIV2, &rreq->flags)) { + if (netfs_folio_group(folio) == NETFS_FOLIO_COPY_TO_CACHE) { + trace_netfs_folio(folio, netfs_folio_trace_sched_copy); + folio_mark_dirty(folio); } else { + finfo = netfs_folio_info(folio); + if (finfo) { + trace_netfs_folio(folio, netfs_folio_trace_filled_gaps); + if (finfo->netfs_group) + folio_change_private(folio, finfo->netfs_group); + else + folio_detach_private(folio); + kfree(finfo); + } trace_netfs_folio(folio, netfs_folio_trace_read_done); } folioq_clear(folioq, slot); } else { // TODO: Use of PG_private_2 is deprecated. - if (test_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &rreq->flags)) + if (folio_test_private_2(folio)) netfs_pgpriv2_copy_to_cache(rreq, folio); } @@ -95,6 +118,35 @@ just_unlock: } /* + * Determine how much to gather before unlocking more folios. + */ +void netfs_read_set_unlock_at(struct netfs_io_request *rreq) +{ + struct folio_queue *folioq = rreq->buffer.tail; + unsigned int slot = rreq->buffer.first_tail_slot; + size_t cleaned_to = rreq->cleaned_to - rreq->start; + size_t progress_at = cleaned_to; + size_t minimum = 256 * 1024; + + while (progress_at < rreq->len) { + if (slot >= folioq_count(folioq)) { + folioq = folioq->next; + if (!folioq) + break; + slot = 0; + } + + progress_at += folioq_folio_size(folioq, slot); + if (progress_at - cleaned_to >= minimum) + break; + slot++; + } + + WRITE_ONCE(rreq->progress_at, progress_at); + trace_netfs_read_progress_at(rreq); +} + +/* * Unlock any folios we've finished with. */ static void netfs_read_unlock_folios(struct netfs_io_request *rreq, @@ -112,30 +164,31 @@ static void netfs_read_unlock_folios(struct netfs_io_request *rreq, if (slot >= folioq_nr_slots(folioq)) { folioq = rolling_buffer_delete_spent(&rreq->buffer); if (!folioq) { - rreq->front_folio_order = 0; + WRITE_ONCE(rreq->progress_at, rreq->len); return; } slot = 0; } + /* We have to wait for readahead refs to have been released before we + * can unlock any folios as the ref-dropper walks i_pages and the only + * thing preventing these folios from being removed is the folio lock. + */ + if (test_bit(NETFS_RREQ_NEED_PUT_RA_REFS, &rreq->flags)) + netfs_wait_for_put_ra_refs(rreq); + for (;;) { struct folio *folio; unsigned long long fpos, fend; - unsigned int order; size_t fsize; - if (*notes & COPY_TO_CACHE) - set_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &rreq->flags); - folio = folioq_folio(folioq, slot); if (WARN_ONCE(!folio_test_locked(folio), "R=%08x: folio %lx is not locked\n", rreq->debug_id, folio->index)) trace_netfs_folio(folio, netfs_folio_trace_not_locked); - order = folioq_folio_order(folioq, slot); - rreq->front_folio_order = order; - fsize = PAGE_SIZE << order; + fsize = folioq_folio_size(folioq, slot); fpos = folio_pos(folio); fend = fpos + fsize; @@ -149,8 +202,6 @@ static void netfs_read_unlock_folios(struct netfs_io_request *rreq, WRITE_ONCE(rreq->cleaned_to, fpos + fsize); *notes |= MADE_PROGRESS; - clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &rreq->flags); - /* Clean up the head folioq. If we clear an entire folioq, then * we can get rid of it provided it's not also the tail folioq * being filled by the issuer. @@ -172,6 +223,8 @@ static void netfs_read_unlock_folios(struct netfs_io_request *rreq, rreq->buffer.tail = folioq; done: rreq->buffer.first_tail_slot = slot; + + netfs_read_set_unlock_at(rreq); } /* @@ -232,7 +285,7 @@ reassess: * subreqs. */ if (notes & BUFFERED) { - size_t fsize = PAGE_SIZE << rreq->front_folio_order; + uoff_t unlock_at = rreq->start + rreq->progress_at; /* Clear the tail of a short read. */ if (!(notes & HIT_PENDING) && @@ -248,16 +301,13 @@ reassess: stream->collected_to = front->start + transferred; rreq->collected_to = stream->collected_to; - if (test_bit(NETFS_SREQ_COPY_TO_CACHE, &front->flags)) - notes |= COPY_TO_CACHE; - if (test_bit(NETFS_SREQ_FAILED, &front->flags)) { rreq->abandon_to = front->start + front->len; front->transferred = front->len; transferred = front->len; trace_netfs_rreq(rreq, netfs_rreq_trace_set_abandon); } - if (front->start + transferred >= rreq->cleaned_to + fsize || + if (front->start + transferred >= unlock_at || test_bit(NETFS_SREQ_HIT_EOF, &front->flags)) netfs_read_unlock_folios(rreq, ¬es); } else { @@ -477,20 +527,22 @@ void netfs_read_collection_worker(struct work_struct *work) void netfs_read_subreq_progress(struct netfs_io_subrequest *subreq) { struct netfs_io_request *rreq = subreq->rreq; - struct netfs_io_stream *stream = &rreq->io_streams[0]; - size_t fsize = PAGE_SIZE << rreq->front_folio_order; - - trace_netfs_sreq(subreq, netfs_sreq_trace_progress); + struct netfs_io_stream *stream = &rreq->io_streams[subreq->stream_nr]; + size_t progress_at = READ_ONCE(rreq->progress_at); + uoff_t update_at = rreq->start + progress_at; + uoff_t transferred_to = subreq->start + subreq->transferred; /* If we are at the head of the queue, wake up the collector, * getting a ref to it if we were the ones to do so. */ - if (subreq->start + subreq->transferred > rreq->cleaned_to + fsize && + if (progress_at < rreq->len && + transferred_to >= update_at && (rreq->origin == NETFS_READAHEAD || rreq->origin == NETFS_READPAGE || rreq->origin == NETFS_READ_FOR_WRITE) && list_is_first(&subreq->rreq_link, &stream->subrequests) ) { + trace_netfs_sreq(subreq, netfs_sreq_trace_progress); __set_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags); netfs_wake_collector(rreq); } diff --git a/fs/netfs/read_pgpriv2.c b/fs/netfs/read_pgpriv2.c index c31190993b76..a4b7bb88cbdb 100644 --- a/fs/netfs/read_pgpriv2.c +++ b/fs/netfs/read_pgpriv2.c @@ -54,8 +54,8 @@ static void netfs_pgpriv2_copy_folio(struct netfs_io_request *creq, struct folio /* Attach the folio to the rolling buffer. */ if (rolling_buffer_append(&creq->buffer, folio, 0, creq->gfp) < 0) { + set_bit(NETFS_RREQ_CANCEL_CACHING, &creq->flags); folio_end_private_2(folio); - clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &creq->flags); return; } @@ -122,13 +122,14 @@ cancel_put: netfs_put_failed_request(creq); cancel: rreq->copy_to_cache = ERR_PTR(-ENOBUFS); - clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &rreq->flags); + set_bit(NETFS_RREQ_CANCEL_CACHING, &rreq->flags); return ERR_PTR(-ENOBUFS); } /* * [DEPRECATED] Mark page as requiring copy-to-cache using PG_private_2 and add - * it to the copy write request. + * it to the copy write request. PG_private_2 should already be set on the + * folio. */ void netfs_pgpriv2_copy_to_cache(struct netfs_io_request *rreq, struct folio *folio) { @@ -136,11 +137,13 @@ void netfs_pgpriv2_copy_to_cache(struct netfs_io_request *rreq, struct folio *fo if (!creq) creq = netfs_pgpriv2_begin_copy_to_cache(rreq, folio); - if (IS_ERR(creq)) + if (IS_ERR(creq)) { + set_bit(NETFS_RREQ_CANCEL_CACHING, &rreq->flags); + netfs_cancel_copy_to_cache(rreq, folio); return; + } - trace_netfs_folio(folio, netfs_folio_trace_copy_to_cache); - folio_start_private_2(folio); + trace_netfs_folio(folio, netfs_folio_trace_pgpriv2_copy); netfs_pgpriv2_copy_folio(creq, folio); } diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c index 2b42758e01ec..4f6a36c6e214 100644 --- a/fs/netfs/read_retry.c +++ b/fs/netfs/read_retry.c @@ -292,11 +292,22 @@ void netfs_unlock_abandoned_read_pages(struct netfs_io_request *rreq) { struct folio_queue *p; + /* We have to wait for readahead refs to have been released before we + * can unlock any folios as the ref-dropper walks i_pages and the only + * thing preventing these folios from being removed is the folio lock. + */ + if (test_bit(NETFS_RREQ_NEED_PUT_RA_REFS, &rreq->flags)) + netfs_wait_for_put_ra_refs(rreq); + for (p = rreq->buffer.tail; p; p = p->next) { for (int slot = 0; slot < folioq_count(p); slot++) { struct folio *folio = folioq_folio(p, slot); - if (folio && !folioq_is_marked2(p, slot)) { + if (!folio) + continue; + netfs_cancel_copy_to_cache(rreq, folio); + + if (!folioq_is_marked2(p, slot)) { if (folio == rreq->no_unlock_folio && test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags)) { diff --git a/fs/netfs/read_single.c b/fs/netfs/read_single.c index 8833550d2eb6..de67ac41548d 100644 --- a/fs/netfs/read_single.c +++ b/fs/netfs/read_single.c @@ -170,6 +170,8 @@ ssize_t netfs_read_single(struct inode *inode, struct file *file, struct iov_ite if (IS_ERR(rreq)) return PTR_ERR(rreq); + rreq->progress_at = rreq->len; + ret = netfs_single_begin_cache_read(rreq, ictx); if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS) goto cleanup_free; diff --git a/fs/netfs/rolling_buffer.c b/fs/netfs/rolling_buffer.c index 8c0026836f9c..424e77a9a109 100644 --- a/fs/netfs/rolling_buffer.c +++ b/fs/netfs/rolling_buffer.c @@ -115,42 +115,65 @@ int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp) } /* - * Decant the list of folios to read into a rolling buffer. + * Decant the entire list of folios to read into a rolling buffer. */ -ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll, - struct readahead_control *ractl, - struct folio_batch *put_batch) +ssize_t rolling_buffer_bulk_load_from_ra(struct rolling_buffer *roll, + struct readahead_control *ractl, + unsigned int rreq_id, gfp_t gfp) { struct folio_queue *fq; - struct page **vec; - int nr, ix, to; - ssize_t size = 0; + ssize_t loaded = 0; - if (rolling_buffer_make_space(roll, GFP_KERNEL) < 0) - return -ENOMEM; + while (ractl->_nr_pages - ractl->_batch_count > 0) { + unsigned int nr; - fq = roll->head; - vec = (struct page **)fq->vec.folios; - nr = __readahead_batch(ractl, vec + folio_batch_count(&fq->vec), - folio_batch_space(&fq->vec)); - ix = fq->vec.nr; - to = ix + nr; - fq->vec.nr = to; - for (; ix < to; ix++) { - struct folio *folio = folioq_folio(fq, ix); - unsigned int order = folio_order(folio); - - fq->orders[ix] = order; - size += PAGE_SIZE << order; - trace_netfs_folio(folio, netfs_folio_trace_read); - if (!folio_batch_add(put_batch, folio)) - folio_batch_release(put_batch); + /* Allocate a folioq to put some folios into and attach it to + * the rolling buffer. + */ + fq = netfs_folioq_alloc(rreq_id, gfp, + netfs_trace_folioq_make_space); + if (!fq) + goto nomem_unlock; + fq->prev = roll->head; + if (!roll->tail) + roll->tail = fq; + else + roll->head->next = fq; + roll->head = fq; + + /* Get a batch of folios and note their orders. */ + nr = __readahead_batch(ractl, (struct page **)fq->vec.folios, + folioq_nr_slots(fq)); + if (WARN_ON_ONCE(!nr)) + break; + fq->vec.nr = nr; + + for (int slot = 0; slot < nr; slot++) { + struct folio *folio = folioq_folio(fq, slot); + unsigned int order; + + order = folio_order(folio); + fq->orders[slot] = order; + loaded += PAGE_SIZE << order; + trace_netfs_folio(folio, netfs_folio_trace_read); + } } - WRITE_ONCE(roll->iter.count, roll->iter.count + size); - /* Store the counter after setting the slot. */ - smp_store_release(&roll->next_head_slot, to); - return size; + WRITE_ONCE(roll->iter.count, loaded); + iov_iter_folio_queue(&roll->iter, ITER_DEST, roll->tail, 0, 0, loaded); + return loaded; + +nomem_unlock: + for (fq = roll->tail; fq; fq = fq->next) { + for (int slot = 0; slot < folioq_count(fq); slot++) { + folio_unlock(fq->vec.folios[slot]); + folioq_mark(fq, slot); + } + } + rolling_buffer_clear(roll); + roll->head = NULL; + roll->tail = NULL; + return -ENOMEM; } /* diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 2d9cfcd43658..851f6f93ad45 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -170,6 +170,8 @@ void netfs_prepare_write(struct netfs_io_request *wreq, rolling_buffer_make_space(&wreq->buffer, wreq->gfp); subreq = netfs_alloc_subrequest(wreq); + if (!subreq) + return; subreq->source = stream->source; subreq->start = start; subreq->stream_nr = stream->stream_nr; diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index e487597337e8..bd0a3f9039d2 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1543,7 +1543,7 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc) struct ovl_fs *ofs = sb->s_fs_info; int err; - err = -EIO; + err = -EINVAL; /* The fscontext fd may have been passed to another user namespace. */ if (fc->user_ns != current_user_ns()) goto out_err; diff --git a/fs/super.c b/fs/super.c index 05e443173038..01db6124e409 100644 --- a/fs/super.c +++ b/fs/super.c @@ -2369,11 +2369,14 @@ static int thaw_super_locked(struct super_block *sb, enum freeze_holder who, goto out_unlock; /* - * All freezers share a single active reference. - * So just unlock in case there are any left. + * All freezers share a single active reference. If other freezers + * remain, drop our hold and report success; the superblock stays + * frozen until the last holder thaws it. */ - if (freeze_dec(sb, who)) + if (freeze_dec(sb, who)) { + error = 0; goto out_unlock; + } if (sb_rdonly(sb)) { sb->s_writers.frozen = SB_UNFROZEN; diff --git a/fs/ufs/cylinder.c b/fs/ufs/cylinder.c index a2813270c303..b930ee1cf853 100644 --- a/fs/ufs/cylinder.c +++ b/fs/ufs/cylinder.c @@ -68,6 +68,16 @@ static bool ufs_read_cylinder(struct super_block *sb, ucpi->c_clustersumoff = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_clustersumoff); ucpi->c_clusteroff = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_clusteroff); ucpi->c_nclusterblks = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_nclusterblks); + + /* these on-disk values become array and bitmap indices */ + if (ucpi->c_cgx != cgno || + ucpi->c_rotor >= uspi->s_fpg || + ucpi->c_frotor >= uspi->s_fpg || + ucpi->c_irotor >= uspi->s_ipg) { + ufs_error(sb, __func__, + "inconsistent metadata in cylinder group %u\n", cgno); + goto failed; + } UFSD("EXIT\n"); return true; diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c index e62fe5667671..ce43cf20b07c 100644 --- a/fs/ufs/dir.c +++ b/fs/ufs/dir.c @@ -590,7 +590,7 @@ int ufs_empty_dir(struct inode * inode) kaddr = ufs_get_folio(inode, i, &folio); if (IS_ERR(kaddr)) - continue; + return 0; de = (struct ufs_dir_entry *)kaddr; kaddr += ufs_last_byte(inode, i) - UFS_DIR_REC_LEN(1); diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 6dcf6d048cce..3569ac92b065 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -1199,6 +1199,15 @@ magic_found: sb->s_maxbytes = ufs_max_bytes(sb); sb->s_max_links = UFS_LINK_MAX; + ufs_setup_cstotal(sb); + /* + * Read cylinder group structures + */ + if (!sb_rdonly(sb)) + if (!ufs_read_cylinder_structures(sb)) + goto failed; + + /* create the root dentry last, once UFS_SB(sb) is fully set up */ inode = ufs_iget(sb, UFS_ROOTINO); if (IS_ERR(inode)) { ret = PTR_ERR(inode); @@ -1210,14 +1219,6 @@ magic_found: goto failed; } - ufs_setup_cstotal(sb); - /* - * Read cylinder group structures - */ - if (!sb_rdonly(sb)) - if (!ufs_read_cylinder_structures(sb)) - goto failed; - UFSD("EXIT\n"); return 0; diff --git a/include/linux/netfs.h b/include/linux/netfs.h index f837a501008c..b4dd32863dd4 100644 --- a/include/linux/netfs.h +++ b/include/linux/netfs.h @@ -246,6 +246,7 @@ struct netfs_io_request { unsigned long long submitted; /* Amount submitted for I/O so far */ unsigned long long len; /* Length of the request */ size_t transferred; /* Amount to be indicated as transferred */ + size_t progress_at; /* Report read progress when hit this much read */ long error; /* 0 or error that occurred */ unsigned long long i_size; /* Size of the file */ unsigned long long start; /* Start position */ @@ -262,7 +263,6 @@ struct netfs_io_request { atomic_t subreq_counter; /* Next subreq->debug_index */ unsigned int nr_group_rel; /* Number of refs to release on ->group */ spinlock_t lock; /* Lock for queuing subreqs */ - unsigned char front_folio_order; /* Order (size) of front folio */ enum netfs_io_origin origin; /* Origin of the request */ bool direct_bv_unpin; /* T if direct_bv[] must be unpinned */ refcount_t ref; @@ -275,9 +275,10 @@ struct netfs_io_request { #define NETFS_RREQ_SHORT_TRANSFER 5 /* Set if we have a short transfer */ #define NETFS_RREQ_OFFLOAD_COLLECTION 8 /* Offload collection to workqueue */ #define NETFS_RREQ_NO_UNLOCK_FOLIO 9 /* Don't unlock no_unlock_folio on completion */ -#define NETFS_RREQ_FOLIO_COPY_TO_CACHE 10 /* Copy current folio to cache from read */ +#define NETFS_RREQ_CANCEL_CACHING 10 /* Set to cancel caching */ #define NETFS_RREQ_UPLOAD_TO_SERVER 11 /* Need to write to the server */ #define NETFS_RREQ_USE_IO_ITER 12 /* Use ->io_iter rather than ->i_pages */ +#define NETFS_RREQ_NEED_PUT_RA_REFS 17 /* Need to put the folio refs RA gave us */ #define NETFS_RREQ_USE_PGPRIV2 31 /* [DEPRECATED] Use PG_private_2 to mark * write to cache on read */ const struct netfs_request_ops *netfs_ops; diff --git a/include/linux/ns/ns_common_types.h b/include/linux/ns/ns_common_types.h index ea45c54e4435..6ed6b497831c 100644 --- a/include/linux/ns/ns_common_types.h +++ b/include/linux/ns/ns_common_types.h @@ -116,10 +116,8 @@ struct ns_common { struct dentry *stashed; const struct proc_ns_operations *ops; unsigned int inum; - union { - struct ns_tree; - struct rcu_head ns_rcu; - }; + struct ns_tree; + struct rcu_head ns_rcu; }; #define to_ns_common(__ns) \ diff --git a/include/linux/rolling_buffer.h b/include/linux/rolling_buffer.h index 9e5dad29669c..a97f7cfaacaa 100644 --- a/include/linux/rolling_buffer.h +++ b/include/linux/rolling_buffer.h @@ -45,9 +45,9 @@ struct rolling_buffer_snapshot { int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id, unsigned int direction, gfp_t gfp); int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp); -ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll, - struct readahead_control *ractl, - struct folio_batch *put_batch); +ssize_t rolling_buffer_bulk_load_from_ra(struct rolling_buffer *roll, + struct readahead_control *ractl, + unsigned int rreq_id, gfp_t gfp); ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio, unsigned int flags, gfp_t gfp); struct folio_queue *rolling_buffer_delete_spent(struct rolling_buffer *roll); diff --git a/include/linux/sched.h b/include/linux/sched.h index 8b3d47a325cc..705970d07614 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1787,7 +1787,7 @@ static inline bool is_lazy_mmu_mode_active(void) } #endif -extern struct pid *cad_pid; +extern struct pid __rcu *cad_pid; /* * Per process flags diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h index 584ae88b435e..d45a5476b97d 100644 --- a/include/linux/sched/signal.h +++ b/include/linux/sched/signal.h @@ -562,10 +562,7 @@ static inline sigset_t *sigmask_to_save(void) return res; } -static inline int kill_cad_pid(int sig, int priv) -{ - return kill_pid(cad_pid, sig, priv); -} +int kill_cad_pid(int sig, int priv); /* These can be the second arg to send_sig_info/send_group_sig_info. */ #define SEND_SIG_NOINFO ((struct kernel_siginfo *) 0) diff --git a/include/trace/events/cachefiles.h b/include/trace/events/cachefiles.h index 9259bc71049e..e3101410e8b2 100644 --- a/include/trace/events/cachefiles.h +++ b/include/trace/events/cachefiles.h @@ -372,7 +372,7 @@ TRACE_EVENT(cachefiles_rename, TRACE_EVENT(cachefiles_coherency, TP_PROTO(struct cachefiles_object *obj, ino_t ino, - u64 disk_aux, + const void *disk_aux, enum cachefiles_content content, enum cachefiles_coherency_trace why), @@ -389,12 +389,27 @@ TRACE_EVENT(cachefiles_coherency, ), TP_fast_assign( + union { + __be16 s[4]; + __be64 ll; + } x; + __entry->obj = obj->debug_id; __entry->why = why; __entry->content = content; __entry->ino = ino; __entry->aux = be64_to_cpup((__be64 *)obj->cookie->inline_aux); - __entry->disk_aux = disk_aux; + + /* cachefiles_xattr::data is 2-byte aligned but not 8-byte aligned. */ + if (disk_aux) { + x.s[0] = ((__be16 *)disk_aux)[0]; + x.s[1] = ((__be16 *)disk_aux)[1]; + x.s[2] = ((__be16 *)disk_aux)[2]; + x.s[3] = ((__be16 *)disk_aux)[3]; + __entry->disk_aux = be64_to_cpu(x.ll); + } else { + __entry->disk_aux = 0; + } ), TP_printk("o=%08x %s B=%llx c=%u aux=%llx dsk=%llx", diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h index 082cb03c6131..3fec3e8f91c8 100644 --- a/include/trace/events/netfs.h +++ b/include/trace/events/netfs.h @@ -59,6 +59,7 @@ EM(netfs_rreq_trace_free, "FREE ") \ EM(netfs_rreq_trace_intr, "INTR ") \ EM(netfs_rreq_trace_ki_complete, "KI-CMPL") \ + EM(netfs_rreq_trace_ra_put_ref, "RA-PUT ") \ EM(netfs_rreq_trace_recollect, "RECLLCT") \ EM(netfs_rreq_trace_redirty, "REDIRTY") \ EM(netfs_rreq_trace_resubmit, "RESUBMT") \ @@ -70,9 +71,11 @@ EM(netfs_rreq_trace_unpause, "UNPAUSE") \ EM(netfs_rreq_trace_wait_ip, "WAIT-IP") \ EM(netfs_rreq_trace_wait_pause, "--PAUSED--") \ + EM(netfs_rreq_trace_wait_put_ra_refs, "WAIT-P-RA") \ EM(netfs_rreq_trace_wait_quiesce, "WAIT-QUIESCE") \ EM(netfs_rreq_trace_waited_ip, "DONE-IP") \ EM(netfs_rreq_trace_waited_pause, "--UNPAUSED--") \ + EM(netfs_rreq_trace_waited_put_ra_refs, "DONE-P-RA") \ EM(netfs_rreq_trace_waited_quiesce, "DONE-QUIESCE") \ EM(netfs_rreq_trace_wake_ip, "WAKE-IP") \ EM(netfs_rreq_trace_wake_queue, "WAKE-Q ") \ @@ -195,7 +198,6 @@ EM(netfs_folio_trace_clear_cc, "clear-cc") \ EM(netfs_folio_trace_clear_g, "clear-g") \ EM(netfs_folio_trace_clear_s, "clear-s") \ - EM(netfs_folio_trace_copy_to_cache, "mark-copy") \ EM(netfs_folio_trace_end_copy, "end-copy") \ EM(netfs_folio_trace_filled_gaps, "filled-gaps") \ EM(netfs_folio_trace_invalidate_all, "inval-all") \ @@ -206,16 +208,19 @@ EM(netfs_folio_trace_kill_cc, "kill-cc") \ EM(netfs_folio_trace_kill_g, "kill-g") \ EM(netfs_folio_trace_kill_s, "kill-s") \ + EM(netfs_folio_trace_mark_copy, "mark-copy") \ EM(netfs_folio_trace_mkwrite, "mkwrite") \ EM(netfs_folio_trace_mkwrite_plus, "mkwrite+") \ - EM(netfs_folio_trace_not_under_wback, "!wback") \ EM(netfs_folio_trace_not_locked, "!locked") \ + EM(netfs_folio_trace_not_under_wback, "!wback") \ + EM(netfs_folio_trace_pgpriv2_copy, "pgpriv2-copy") \ EM(netfs_folio_trace_put, "put") \ EM(netfs_folio_trace_read, "read") \ EM(netfs_folio_trace_read_done, "read-done") \ EM(netfs_folio_trace_read_gaps, "read-gaps") \ EM(netfs_folio_trace_read_unlock, "read-unlock") \ EM(netfs_folio_trace_redirtied, "redirtied") \ + EM(netfs_folio_trace_sched_copy, "sched-copy") \ EM(netfs_folio_trace_store, "store") \ EM(netfs_folio_trace_store_copy, "store-copy") \ EM(netfs_folio_trace_store_plus, "store+") \ @@ -786,6 +791,27 @@ TRACE_EVENT(netfs_folioq, __print_symbolic(__entry->trace, netfs_folioq_traces)) ); +TRACE_EVENT(netfs_read_progress_at, + TP_PROTO(const struct netfs_io_request *rreq), + + TP_ARGS(rreq), + + TP_STRUCT__entry( + __field(unsigned int, rreq) + __field(size_t, progress_at) + __field(size_t, cleaned_to) + ), + + TP_fast_assign( + __entry->rreq = rreq->debug_id; + __entry->cleaned_to = rreq->cleaned_to - rreq->start; + __entry->progress_at = rreq->progress_at; + ), + + TP_printk("R=%08x cln=%zx prg=%zx", + __entry->rreq, __entry->cleaned_to, __entry->progress_at) + ); + #undef EM #undef E_ #endif /* _TRACE_NETFS_H */ diff --git a/init/main.c b/init/main.c index 2613d3f9b3ce..c05e63b8ea06 100644 --- a/init/main.c +++ b/init/main.c @@ -1648,7 +1648,7 @@ static noinline void __init kernel_init_freeable(void) */ set_mems_allowed(node_states[N_MEMORY]); - cad_pid = get_pid(task_pid(current)); + rcu_assign_pointer(cad_pid, get_pid(task_pid(current))); smp_prepare_cpus(setup_max_cpus); diff --git a/kernel/reboot.c b/kernel/reboot.c index f070c5c1103a..d177d89fcc33 100644 --- a/kernel/reboot.c +++ b/kernel/reboot.c @@ -13,7 +13,9 @@ #include <linux/kexec.h> #include <linux/kmod.h> #include <linux/kmsg_dump.h> +#include <linux/rcupdate.h> #include <linux/reboot.h> +#include <linux/sched/signal.h> #include <linux/suspend.h> #include <linux/syscalls.h> #include <linux/syscore_ops.h> @@ -24,8 +26,7 @@ */ static int C_A_D = 1; -struct pid *cad_pid; -EXPORT_SYMBOL(cad_pid); +struct pid __rcu *cad_pid; #if defined(CONFIG_ARM) #define DEFAULT_REBOOT_MODE = REBOOT_HARD @@ -1371,10 +1372,14 @@ static int proc_do_cad_pid(const struct ctl_table *table, int write, void *buffe { struct ctl_table tmp_table = *table; struct pid *new_pid; + struct pid *old_pid; pid_t tmp_pid; int r; - tmp_pid = pid_vnr(cad_pid); + rcu_read_lock(); + tmp_pid = pid_vnr(rcu_dereference(cad_pid)); + rcu_read_unlock(); + tmp_table.data = &tmp_pid; r = proc_dointvec(&tmp_table, write, buffer, lenp, ppos); @@ -1385,7 +1390,13 @@ static int proc_do_cad_pid(const struct ctl_table *table, int write, void *buffe if (!new_pid) return -ESRCH; - put_pid(xchg(&cad_pid, new_pid)); + old_pid = unrcu_pointer(xchg(&cad_pid, RCU_INITIALIZER(new_pid))); + /* + * Wait for cad_pid readers before put_pid(). We cannot use + * call_rcu() here because free_pid() already owns pid->rcu. + */ + synchronize_rcu(); + put_pid(old_pid); return 0; } diff --git a/kernel/signal.c b/kernel/signal.c index a5e15bf09d31..ec30550951ec 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1892,6 +1892,18 @@ int kill_pid(struct pid *pid, int sig, int priv) } EXPORT_SYMBOL(kill_pid); +int kill_cad_pid(int sig, int priv) +{ + int ret; + + rcu_read_lock(); + ret = kill_pid(rcu_dereference(cad_pid), sig, priv); + rcu_read_unlock(); + + return ret; +} +EXPORT_SYMBOL(kill_cad_pid); + #ifdef CONFIG_POSIX_TIMERS /* * These functions handle POSIX timer signals. POSIX timers use |
