mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-10-23 07:23:12 +02:00
vfs-6.16-rc2.fixes
-----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCaD1hUAAKCRCRxhvAZXjc ohIBAQCJQYP7bzg+A7C7K6cA+5LwC1u4aZ424B5puIZrLiEEDQEAxQli95/rTIeE m2DWBDl5rMrKlfmpqGvjbbJldU75swo= =2EhD -----END PGP SIGNATURE----- Merge tag 'vfs-6.16-rc2.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull vfs fixes from Christian Brauner: - Fix the AT_HANDLE_CONNECTABLE option so filesystems that don't know how to decode a connected non-dir dentry fail the request - Use repr(transparent) to ensure identical layout between the C and Rust implementation of struct file - Add a missing xas_pause() into the dax code employing wait_entry_unlocked_exclusive() - Fix FOP_DONTCACHE which we disabled for v6.15. A folio could get redirtied and/or scheduled for writeback after the initial dropbehind test. Change the test accordingly to handle these cases so we can re-enable FOP_DONTCACHE again * tag 'vfs-6.16-rc2.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: exportfs: require ->fh_to_parent() to encode connectable file handles rust: file: improve safety comments rust: file: mark `LocalFile` as `repr(transparent)` fs/dax: Fix "don't skip locked entries when scanning entries" iomap: don't lose folio dropbehind state for overwrites mm/filemap: unify dropbehind flag testing and clearing mm/filemap: unify read/write dropbehind naming Revert "Disable FOP_DONTCACHE for now due to bugs" mm/filemap: use filemap_end_dropbehind() for read invalidation mm/filemap: gate dropbehind invalidate on folio !dirty && !writeback
This commit is contained in:
commit
fcd0bb8e99
2
fs/dax.c
2
fs/dax.c
|
@ -257,7 +257,7 @@ static void *wait_entry_unlocked_exclusive(struct xa_state *xas, void *entry)
|
||||||
wq = dax_entry_waitqueue(xas, entry, &ewait.key);
|
wq = dax_entry_waitqueue(xas, entry, &ewait.key);
|
||||||
prepare_to_wait_exclusive(wq, &ewait.wait,
|
prepare_to_wait_exclusive(wq, &ewait.wait,
|
||||||
TASK_UNINTERRUPTIBLE);
|
TASK_UNINTERRUPTIBLE);
|
||||||
xas_pause(xas);
|
xas_reset(xas);
|
||||||
xas_unlock_irq(xas);
|
xas_unlock_irq(xas);
|
||||||
schedule();
|
schedule();
|
||||||
finish_wait(wq, &ewait.wait);
|
finish_wait(wq, &ewait.wait);
|
||||||
|
|
|
@ -1691,6 +1691,8 @@ static int iomap_add_to_ioend(struct iomap_writepage_ctx *wpc,
|
||||||
ioend_flags |= IOMAP_IOEND_UNWRITTEN;
|
ioend_flags |= IOMAP_IOEND_UNWRITTEN;
|
||||||
if (wpc->iomap.flags & IOMAP_F_SHARED)
|
if (wpc->iomap.flags & IOMAP_F_SHARED)
|
||||||
ioend_flags |= IOMAP_IOEND_SHARED;
|
ioend_flags |= IOMAP_IOEND_SHARED;
|
||||||
|
if (folio_test_dropbehind(folio))
|
||||||
|
ioend_flags |= IOMAP_IOEND_DONTCACHE;
|
||||||
if (pos == wpc->iomap.offset && (wpc->iomap.flags & IOMAP_F_BOUNDARY))
|
if (pos == wpc->iomap.offset && (wpc->iomap.flags & IOMAP_F_BOUNDARY))
|
||||||
ioend_flags |= IOMAP_IOEND_BOUNDARY;
|
ioend_flags |= IOMAP_IOEND_BOUNDARY;
|
||||||
|
|
||||||
|
|
|
@ -436,6 +436,25 @@ allocate_blocks:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
xfs_ioend_needs_wq_completion(
|
||||||
|
struct iomap_ioend *ioend)
|
||||||
|
{
|
||||||
|
/* Changing inode size requires a transaction. */
|
||||||
|
if (xfs_ioend_is_append(ioend))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* Extent manipulation requires a transaction. */
|
||||||
|
if (ioend->io_flags & (IOMAP_IOEND_UNWRITTEN | IOMAP_IOEND_SHARED))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* Page cache invalidation cannot be done in irq context. */
|
||||||
|
if (ioend->io_flags & IOMAP_IOEND_DONTCACHE)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
xfs_submit_ioend(
|
xfs_submit_ioend(
|
||||||
struct iomap_writepage_ctx *wpc,
|
struct iomap_writepage_ctx *wpc,
|
||||||
|
@ -460,8 +479,7 @@ xfs_submit_ioend(
|
||||||
memalloc_nofs_restore(nofs_flag);
|
memalloc_nofs_restore(nofs_flag);
|
||||||
|
|
||||||
/* send ioends that might require a transaction to the completion wq */
|
/* send ioends that might require a transaction to the completion wq */
|
||||||
if (xfs_ioend_is_append(ioend) ||
|
if (xfs_ioend_needs_wq_completion(ioend))
|
||||||
(ioend->io_flags & (IOMAP_IOEND_UNWRITTEN | IOMAP_IOEND_SHARED)))
|
|
||||||
ioend->io_bio.bi_end_io = xfs_end_bio;
|
ioend->io_bio.bi_end_io = xfs_end_bio;
|
||||||
|
|
||||||
if (status)
|
if (status)
|
||||||
|
|
|
@ -314,6 +314,9 @@ static inline bool exportfs_can_decode_fh(const struct export_operations *nop)
|
||||||
static inline bool exportfs_can_encode_fh(const struct export_operations *nop,
|
static inline bool exportfs_can_encode_fh(const struct export_operations *nop,
|
||||||
int fh_flags)
|
int fh_flags)
|
||||||
{
|
{
|
||||||
|
if (!nop)
|
||||||
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If a non-decodeable file handle was requested, we only need to make
|
* If a non-decodeable file handle was requested, we only need to make
|
||||||
* sure that filesystem did not opt-out of encoding fid.
|
* sure that filesystem did not opt-out of encoding fid.
|
||||||
|
@ -321,6 +324,13 @@ static inline bool exportfs_can_encode_fh(const struct export_operations *nop,
|
||||||
if (fh_flags & EXPORT_FH_FID)
|
if (fh_flags & EXPORT_FH_FID)
|
||||||
return exportfs_can_encode_fid(nop);
|
return exportfs_can_encode_fid(nop);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If a connectable file handle was requested, we need to make sure that
|
||||||
|
* filesystem can also decode connected file handles.
|
||||||
|
*/
|
||||||
|
if ((fh_flags & EXPORT_FH_CONNECTABLE) && !nop->fh_to_parent)
|
||||||
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If a decodeable file handle was requested, we need to make sure that
|
* If a decodeable file handle was requested, we need to make sure that
|
||||||
* filesystem can also decode file handles.
|
* filesystem can also decode file handles.
|
||||||
|
|
|
@ -2207,7 +2207,7 @@ struct file_operations {
|
||||||
/* Supports asynchronous lock callbacks */
|
/* Supports asynchronous lock callbacks */
|
||||||
#define FOP_ASYNC_LOCK ((__force fop_flags_t)(1 << 6))
|
#define FOP_ASYNC_LOCK ((__force fop_flags_t)(1 << 6))
|
||||||
/* File system supports uncached read/write buffered IO */
|
/* File system supports uncached read/write buffered IO */
|
||||||
#define FOP_DONTCACHE 0 /* ((__force fop_flags_t)(1 << 7)) */
|
#define FOP_DONTCACHE ((__force fop_flags_t)(1 << 7))
|
||||||
|
|
||||||
/* Wrap a directory iterator that needs exclusive inode access */
|
/* Wrap a directory iterator that needs exclusive inode access */
|
||||||
int wrap_directory_iterator(struct file *, struct dir_context *,
|
int wrap_directory_iterator(struct file *, struct dir_context *,
|
||||||
|
|
|
@ -377,13 +377,16 @@ sector_t iomap_bmap(struct address_space *mapping, sector_t bno,
|
||||||
#define IOMAP_IOEND_BOUNDARY (1U << 2)
|
#define IOMAP_IOEND_BOUNDARY (1U << 2)
|
||||||
/* is direct I/O */
|
/* is direct I/O */
|
||||||
#define IOMAP_IOEND_DIRECT (1U << 3)
|
#define IOMAP_IOEND_DIRECT (1U << 3)
|
||||||
|
/* is DONTCACHE I/O */
|
||||||
|
#define IOMAP_IOEND_DONTCACHE (1U << 4)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Flags that if set on either ioend prevent the merge of two ioends.
|
* Flags that if set on either ioend prevent the merge of two ioends.
|
||||||
* (IOMAP_IOEND_BOUNDARY also prevents merges, but only one-way)
|
* (IOMAP_IOEND_BOUNDARY also prevents merges, but only one-way)
|
||||||
*/
|
*/
|
||||||
#define IOMAP_IOEND_NOMERGE_FLAGS \
|
#define IOMAP_IOEND_NOMERGE_FLAGS \
|
||||||
(IOMAP_IOEND_SHARED | IOMAP_IOEND_UNWRITTEN | IOMAP_IOEND_DIRECT)
|
(IOMAP_IOEND_SHARED | IOMAP_IOEND_UNWRITTEN | IOMAP_IOEND_DIRECT | \
|
||||||
|
IOMAP_IOEND_DONTCACHE)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Structure for writeback I/O completions.
|
* Structure for writeback I/O completions.
|
||||||
|
|
39
mm/filemap.c
39
mm/filemap.c
|
@ -1589,13 +1589,30 @@ int folio_wait_private_2_killable(struct folio *folio)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(folio_wait_private_2_killable);
|
EXPORT_SYMBOL(folio_wait_private_2_killable);
|
||||||
|
|
||||||
|
static void filemap_end_dropbehind(struct folio *folio)
|
||||||
|
{
|
||||||
|
struct address_space *mapping = folio->mapping;
|
||||||
|
|
||||||
|
VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
|
||||||
|
|
||||||
|
if (folio_test_writeback(folio) || folio_test_dirty(folio))
|
||||||
|
return;
|
||||||
|
if (!folio_test_clear_dropbehind(folio))
|
||||||
|
return;
|
||||||
|
if (mapping)
|
||||||
|
folio_unmap_invalidate(mapping, folio, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If folio was marked as dropbehind, then pages should be dropped when writeback
|
* If folio was marked as dropbehind, then pages should be dropped when writeback
|
||||||
* completes. Do that now. If we fail, it's likely because of a big folio -
|
* completes. Do that now. If we fail, it's likely because of a big folio -
|
||||||
* just reset dropbehind for that case and latter completions should invalidate.
|
* just reset dropbehind for that case and latter completions should invalidate.
|
||||||
*/
|
*/
|
||||||
static void folio_end_dropbehind_write(struct folio *folio)
|
static void filemap_end_dropbehind_write(struct folio *folio)
|
||||||
{
|
{
|
||||||
|
if (!folio_test_dropbehind(folio))
|
||||||
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hitting !in_task() should not happen off RWF_DONTCACHE writeback,
|
* Hitting !in_task() should not happen off RWF_DONTCACHE writeback,
|
||||||
* but can happen if normal writeback just happens to find dirty folios
|
* but can happen if normal writeback just happens to find dirty folios
|
||||||
|
@ -1604,8 +1621,7 @@ static void folio_end_dropbehind_write(struct folio *folio)
|
||||||
* invalidation in that case.
|
* invalidation in that case.
|
||||||
*/
|
*/
|
||||||
if (in_task() && folio_trylock(folio)) {
|
if (in_task() && folio_trylock(folio)) {
|
||||||
if (folio->mapping)
|
filemap_end_dropbehind(folio);
|
||||||
folio_unmap_invalidate(folio->mapping, folio, 0);
|
|
||||||
folio_unlock(folio);
|
folio_unlock(folio);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1620,8 +1636,6 @@ static void folio_end_dropbehind_write(struct folio *folio)
|
||||||
*/
|
*/
|
||||||
void folio_end_writeback(struct folio *folio)
|
void folio_end_writeback(struct folio *folio)
|
||||||
{
|
{
|
||||||
bool folio_dropbehind = false;
|
|
||||||
|
|
||||||
VM_BUG_ON_FOLIO(!folio_test_writeback(folio), folio);
|
VM_BUG_ON_FOLIO(!folio_test_writeback(folio), folio);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1643,14 +1657,11 @@ void folio_end_writeback(struct folio *folio)
|
||||||
* reused before the folio_wake_bit().
|
* reused before the folio_wake_bit().
|
||||||
*/
|
*/
|
||||||
folio_get(folio);
|
folio_get(folio);
|
||||||
if (!folio_test_dirty(folio))
|
|
||||||
folio_dropbehind = folio_test_clear_dropbehind(folio);
|
|
||||||
if (__folio_end_writeback(folio))
|
if (__folio_end_writeback(folio))
|
||||||
folio_wake_bit(folio, PG_writeback);
|
folio_wake_bit(folio, PG_writeback);
|
||||||
acct_reclaim_writeback(folio);
|
|
||||||
|
|
||||||
if (folio_dropbehind)
|
filemap_end_dropbehind_write(folio);
|
||||||
folio_end_dropbehind_write(folio);
|
acct_reclaim_writeback(folio);
|
||||||
folio_put(folio);
|
folio_put(folio);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(folio_end_writeback);
|
EXPORT_SYMBOL(folio_end_writeback);
|
||||||
|
@ -2635,16 +2646,14 @@ static inline bool pos_same_folio(loff_t pos1, loff_t pos2, struct folio *folio)
|
||||||
return (pos1 >> shift == pos2 >> shift);
|
return (pos1 >> shift == pos2 >> shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void filemap_end_dropbehind_read(struct address_space *mapping,
|
static void filemap_end_dropbehind_read(struct folio *folio)
|
||||||
struct folio *folio)
|
|
||||||
{
|
{
|
||||||
if (!folio_test_dropbehind(folio))
|
if (!folio_test_dropbehind(folio))
|
||||||
return;
|
return;
|
||||||
if (folio_test_writeback(folio) || folio_test_dirty(folio))
|
if (folio_test_writeback(folio) || folio_test_dirty(folio))
|
||||||
return;
|
return;
|
||||||
if (folio_trylock(folio)) {
|
if (folio_trylock(folio)) {
|
||||||
if (folio_test_clear_dropbehind(folio))
|
filemap_end_dropbehind(folio);
|
||||||
folio_unmap_invalidate(mapping, folio, 0);
|
|
||||||
folio_unlock(folio);
|
folio_unlock(folio);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2765,7 +2774,7 @@ put_folios:
|
||||||
for (i = 0; i < folio_batch_count(&fbatch); i++) {
|
for (i = 0; i < folio_batch_count(&fbatch); i++) {
|
||||||
struct folio *folio = fbatch.folios[i];
|
struct folio *folio = fbatch.folios[i];
|
||||||
|
|
||||||
filemap_end_dropbehind_read(mapping, folio);
|
filemap_end_dropbehind_read(folio);
|
||||||
folio_put(folio);
|
folio_put(folio);
|
||||||
}
|
}
|
||||||
folio_batch_init(&fbatch);
|
folio_batch_init(&fbatch);
|
||||||
|
|
|
@ -219,12 +219,13 @@ unsafe impl AlwaysRefCounted for File {
|
||||||
/// must be on the same thread as this file.
|
/// must be on the same thread as this file.
|
||||||
///
|
///
|
||||||
/// [`assume_no_fdget_pos`]: LocalFile::assume_no_fdget_pos
|
/// [`assume_no_fdget_pos`]: LocalFile::assume_no_fdget_pos
|
||||||
|
#[repr(transparent)]
|
||||||
pub struct LocalFile {
|
pub struct LocalFile {
|
||||||
inner: Opaque<bindings::file>,
|
inner: Opaque<bindings::file>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: The type invariants guarantee that `LocalFile` is always ref-counted. This implementation
|
// SAFETY: The type invariants guarantee that `LocalFile` is always ref-counted. This implementation
|
||||||
// makes `ARef<File>` own a normal refcount.
|
// makes `ARef<LocalFile>` own a normal refcount.
|
||||||
unsafe impl AlwaysRefCounted for LocalFile {
|
unsafe impl AlwaysRefCounted for LocalFile {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn inc_ref(&self) {
|
fn inc_ref(&self) {
|
||||||
|
@ -235,7 +236,8 @@ unsafe impl AlwaysRefCounted for LocalFile {
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn dec_ref(obj: ptr::NonNull<LocalFile>) {
|
unsafe fn dec_ref(obj: ptr::NonNull<LocalFile>) {
|
||||||
// SAFETY: To call this method, the caller passes us ownership of a normal refcount, so we
|
// SAFETY: To call this method, the caller passes us ownership of a normal refcount, so we
|
||||||
// may drop it. The cast is okay since `File` has the same representation as `struct file`.
|
// may drop it. The cast is okay since `LocalFile` has the same representation as
|
||||||
|
// `struct file`.
|
||||||
unsafe { bindings::fput(obj.cast().as_ptr()) }
|
unsafe { bindings::fput(obj.cast().as_ptr()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,7 +275,7 @@ impl LocalFile {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn from_raw_file<'a>(ptr: *const bindings::file) -> &'a LocalFile {
|
pub unsafe fn from_raw_file<'a>(ptr: *const bindings::file) -> &'a LocalFile {
|
||||||
// SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the
|
// SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the
|
||||||
// duration of 'a. The cast is okay because `File` is `repr(transparent)`.
|
// duration of `'a`. The cast is okay because `LocalFile` is `repr(transparent)`.
|
||||||
//
|
//
|
||||||
// INVARIANT: The caller guarantees that there are no problematic `fdget_pos` calls.
|
// INVARIANT: The caller guarantees that there are no problematic `fdget_pos` calls.
|
||||||
unsafe { &*ptr.cast() }
|
unsafe { &*ptr.cast() }
|
||||||
|
@ -347,7 +349,7 @@ impl File {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn from_raw_file<'a>(ptr: *const bindings::file) -> &'a File {
|
pub unsafe fn from_raw_file<'a>(ptr: *const bindings::file) -> &'a File {
|
||||||
// SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the
|
// SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the
|
||||||
// duration of 'a. The cast is okay because `File` is `repr(transparent)`.
|
// duration of `'a`. The cast is okay because `File` is `repr(transparent)`.
|
||||||
//
|
//
|
||||||
// INVARIANT: The caller guarantees that there are no problematic `fdget_pos` calls.
|
// INVARIANT: The caller guarantees that there are no problematic `fdget_pos` calls.
|
||||||
unsafe { &*ptr.cast() }
|
unsafe { &*ptr.cast() }
|
||||||
|
|
Loading…
Reference in New Issue
Block a user