Skip to content

Conversation

@Avenger-285714
Copy link
Member

@Avenger-285714 Avenger-285714 commented Dec 17, 2025

[ Upstream commit e1b4c6a58304fd490124cc2b454d80edc786665c ]

maple_tree insertions can fail if we are seriously short on memory; simple_offset_rename() does not recover well if it runs into that. The same goes for simple_offset_rename_exchange().

Moreover, shmem_whiteout() expects that if it succeeds, the caller will progress to d_move(), i.e. that shmem_rename2() won't fail past the successful call of shmem_whiteout().

Not hard to fix, fortunately - mtree_store() can't fail if the index we are trying to store into is already present in the tree as a singleton.

For simple_offset_rename_exchange() that's enough - we just need to be careful about the order of operations.

For simple_offset_rename() solution is to preinsert the target into the tree for new_dir; the rest can be done without any potentially failing operations.

That preinsertion has to be done in shmem_rename2() rather than in simple_offset_rename() itself - otherwise we'd need to deal with the possibility of failure after successful shmem_whiteout().

Fixes: a2e4595 ("shmem: stable directory offsets")
Reviewed-by: Christian Brauner [email protected]
Reviewed-by: Chuck Lever [email protected]

[ Backport from v6.19 ]

Summary by Sourcery

Improve shmem rename recovery when maple_tree operations fail under low-memory conditions.

Bug Fixes:

  • Ensure simple_offset_rename is preceded by a preallocated offset slot and no longer fails mid-rename, preventing inconsistent shmem directory offsets.
  • Make simple_offset_rename_exchange update maple_tree entries in a failure-safe order and restore state correctly if mtree_store calls fail.
  • Prevent shmem_rename2 from leaving stale offset entries on whiteout failures by pre-inserting and conditionally removing the new dentry offset slot.

Enhancements:

  • Change simple_offset_rename to a void helper with internal WARN-based checks now that its callers pre-grab maple_tree slots.

@sourcery-ai
Copy link

sourcery-ai bot commented Dec 17, 2025

Reviewer's Guide

Adjusts shmem/simple directory offset handling so rename and rename-exchange operations pre-reserve maple_tree slots and avoid failing after whiteout or partially applied offset updates, ensuring consistent recovery when maple_tree insertions fail under low memory conditions.

Sequence diagram for shmem_rename2 rename offset handling

sequenceDiagram
    participant VFS
    participant shmem_rename2
    participant new_dir_ctx
    participant old_dir_ctx
    participant simple_offset_rename
    participant shmem_whiteout

    VFS->>shmem_rename2: shmem_rename2(idmap, old_dir, old_dentry, new_dir, new_dentry, flags)
    activate shmem_rename2

    alt invalid_flags_or_nonempty_target
        shmem_rename2-->>VFS: error
        deactivate shmem_rename2
    else valid_and_empty
        shmem_rename2->>new_dir_ctx: simple_offset_add(new_dir_ctx, new_dentry)
        alt simple_offset_add_returns_EBUSY
            shmem_rename2->>shmem_rename2: had_offset = true
        else simple_offset_add_returns_error
            shmem_rename2-->>VFS: error
            activate shmem_rename2
            deactivate shmem_rename2
        end

        alt flags_has_RENAME_WHITEOUT
            shmem_rename2->>shmem_whiteout: shmem_whiteout(idmap, old_dir, old_dentry)
            alt shmem_whiteout_error
                shmem_rename2->>shmem_rename2: if !had_offset remove preinserted slot
                shmem_rename2->>new_dir_ctx: simple_offset_remove(new_dir_ctx, new_dentry)
                shmem_rename2-->>VFS: error
                activate shmem_rename2
                deactivate shmem_rename2
            else shmem_whiteout_success
                shmem_rename2->>simple_offset_rename: simple_offset_rename(old_dir, old_dentry, new_dir, new_dentry)
            end
        else no_RENAME_WHITEOUT
            shmem_rename2->>simple_offset_rename: simple_offset_rename(old_dir, old_dentry, new_dir, new_dentry)
        end

        simple_offset_rename-->>shmem_rename2: void (offsets updated, no failures)

        shmem_rename2->>shmem_rename2: if d_really_is_positive(new_dentry) then shmem_unlink, directory_updates
        shmem_rename2-->>VFS: success
        activate shmem_rename2
        deactivate shmem_rename2
    end
Loading

Updated class diagram for simple offset and shmem rename interactions

classDiagram
    class inode {
        +i_mode: umode_t
        +i_op: inode_operations*
    }

    class inode_operations {
        +get_offset_ctx(inode inode*): offset_ctx*
    }

    class offset_ctx {
        +mt: maple_tree
    }

    class maple_tree {
        +root: void*
    }

    class SimpleOffsetOps {
        +simple_offset_init(octx offset_ctx*): void
        +simple_offset_add(octx offset_ctx*, dentry dentry*): int
        +simple_offset_remove(octx offset_ctx*, dentry dentry*): void
        +simple_offset_rename(old_dir inode*, old_dentry dentry*, new_dir inode*, new_dentry dentry*): void
        +simple_offset_rename_exchange(old_dir inode*, old_dentry dentry*, new_dir inode*, new_dentry dentry*): int
        +simple_offset_replace(octx offset_ctx*, dentry dentry*, index u32): int
    }

    class ShmemRename {
        +shmem_rename2(idmap mnt_idmap*, old_dir inode*, old_dentry dentry*, new_dir inode*, new_dentry dentry*, flags unsigned int): int
        +shmem_whiteout(idmap mnt_idmap*, dir inode*, dentry dentry*): int
        +shmem_get_offset_ctx(dir inode*): offset_ctx*
    }

    class DentryHelpers {
        +dentry2offset(dentry dentry*): u32
        +offset_set(dentry dentry*, index u32): void
        +d_really_is_positive(dentry dentry*): bool
    }

    class VfsRenameHelpers {
        +simple_rename_exchange(old_dir inode*, old_dentry dentry*, new_dir inode*, new_dentry dentry*): int
        +simple_empty(dentry dentry*): bool
    }

    class MapleTreeApi {
        +mtree_store(mt maple_tree*, index u32, value void*, gfp gfp_t): int
    }

    inode --> inode_operations : uses
    inode_operations --> offset_ctx : get_offset_ctx
    offset_ctx --> maple_tree : has

    ShmemRename --> offset_ctx : shmem_get_offset_ctx
    ShmemRename --> SimpleOffsetOps : uses
    ShmemRename --> DentryHelpers : uses
    ShmemRename --> VfsRenameHelpers : uses

    SimpleOffsetOps --> DentryHelpers : uses
    SimpleOffsetOps --> MapleTreeApi : uses

    MapleTreeApi --> maple_tree : operates_on

    VfsRenameHelpers --> inode : uses
    VfsRenameHelpers --> DentryHelpers : uses
Loading

Flow diagram for simple_offset_rename_exchange low-memory safe updates

flowchart TD
    A["Start simple_offset_rename_exchange"] --> B["Get old_index = dentry2offset(old_dentry)"]
    B --> C["Get new_index = dentry2offset(new_dentry)"]
    C --> D{"old_index == 0 or new_index == 0?"}
    D -->|Yes| E["WARN_ON and return -EINVAL"]
    D -->|No| F["ret = mtree_store(new_ctx.mt, new_index, old_dentry)"]
    F --> G{"ret != 0?"}
    G -->|Yes| H["WARN_ON and return ret"]
    G -->|No| I["ret = mtree_store(old_ctx.mt, old_index, new_dentry)"]
    I --> J{"ret != 0?"}
    J -->|Yes| K["Restore new_ctx: mtree_store(new_ctx.mt, new_index, new_dentry)"]
    K --> L["return ret"]
    J -->|No| M["offset_set(old_dentry, new_index)"]
    M --> N["offset_set(new_dentry, old_index)"]
    N --> O["simple_rename_exchange(old_dir, old_dentry, new_dir, new_dentry)"]
    O --> P["return 0"]
Loading

File-Level Changes

Change Details Files
Make simple_offset_rename a void helper that assumes the new_dentry offset slot is pre-reserved and simplify its update logic.
  • Change simple_offset_rename() return type from int to void and update its contract comment to require a pre-grabbed maple_tree slot for new_dentry.
  • Guard against zero/invalid new_offset with WARN_ON and early return.
  • Remove old error-handling branches and always remove old_dir entry, clear new_dentry offset, and call simple_offset_replace() with WARN_ON on failure.
fs/libfs.c
include/linux/fs.h
Rework simple_offset_rename_exchange to avoid unrecoverable maple_tree failures by using mtree_store directly and making operations reversible.
  • Validate that both old_index and new_index are non-zero and fail with -EINVAL otherwise.
  • Use mtree_store() to insert old_dentry at new_index and new_dentry at old_index, treating failures as WARN_ON and rolling back the first insertion if the second fails.
  • Update dentries’ offsets locally and call simple_rename_exchange() without additional offset recovery logic, removing the earlier multi-step replace/restore paths.
fs/libfs.c
Preinsert the target dentry into the shmem directory offset tree in shmem_rename2 and ensure proper cleanup on whiteout failures.
  • Introduce a had_offset flag in shmem_rename2() and call simple_offset_add() for new_dentry before any whiteout or rename operations, treating -EBUSY as an already-present offset.
  • On shmem_whiteout() failure, remove the newly-added offset mapping for new_dentry if it was newly created (had_offset == false) before returning the error.
  • Invoke simple_offset_rename() unconditionally (now void) after successful whiteout and preinsertion, relying on the pre-grabbed slot to avoid further allocation failures.
mm/shmem.c

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • The new WARN_ON(!new_offset) / WARN_ON(!old_index || !new_index) assumptions in the offset helpers are subtle; consider adding brief comments documenting why a zero offset is impossible at these points (e.g., because callers have pre-grabbed slots), so future changes don’t inadvertently break these invariants.
  • In simple_offset_rename_exchange(), you now rely on mtree_store() being effectively infallible when the index is already a singleton and use it for rollback; it would help maintainability to add a short code comment explicitly stating this constraint and that GFP_KERNEL is safe in these specific calls.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new WARN_ON(!new_offset) / WARN_ON(!old_index || !new_index) assumptions in the offset helpers are subtle; consider adding brief comments documenting why a zero offset is impossible at these points (e.g., because callers have pre-grabbed slots), so future changes don’t inadvertently break these invariants.
- In simple_offset_rename_exchange(), you now rely on mtree_store() being effectively infallible when the index is already a singleton and use it for rollback; it would help maintainability to add a short code comment explicitly stating this constraint and that GFP_KERNEL is safe in these specific calls.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR backports an upstream fix from v6.19 to the 6.6-y kernel that addresses recovery failures during rename operations in the shmem filesystem. The fix ensures proper cleanup when maple_tree/xarray insertions fail due to memory shortage. However, this backport contains critical compilation errors that must be fixed before merging.

Key Changes:

  • Pre-allocates offset slots for rename targets to avoid failure points after non-reversible operations
  • Changes simple_offset_rename() from returning int to void, moving error handling to callers
  • Updates simple_offset_rename_exchange() to use guaranteed-success operations when slots already exist

Critical Issue: The backport incorrectly uses mtree_store() with non-existent mt fields instead of xa_store() with the actual xa fields in offset_ctx structure.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
mm/shmem.c Pre-allocates offset slot before potentially failing operations (whiteout) and adds cleanup logic on failure
include/linux/fs.h Changes simple_offset_rename() return type from int to void
fs/libfs.c Refactors rename functions to eliminate failure points after slot pre-allocation; contains critical bugs using wrong API

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

fs/libfs.c Outdated
goto out_restore;
ret = mtree_store(&old_ctx->mt, old_index, new_dentry, GFP_KERNEL);
if (WARN_ON(ret)) {
mtree_store(&new_ctx->mt, new_index, new_dentry, GFP_KERNEL);
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code attempts to access new_ctx->mt but the offset_ctx structure only contains an xa (xarray) field, not an mt (maple_tree) field. This will cause a compilation error. The correct field to use is new_ctx->xa with xa_store instead of mtree_store.

Copilot uses AI. Check for mistakes.
* (new) directory entry to be unchanged after a rename.
*
* Returns zero on success, a negative errno value on failure.
* Caller must have grabbed a slot for new_dentry in the maple_tree
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment mentions "maple_tree" but the offset_ctx structure in this kernel version uses xarray (xa) not maple_tree. The comment should refer to "xarray" instead to match the actual data structure being used.

Suggested change
* Caller must have grabbed a slot for new_dentry in the maple_tree
* Caller must have grabbed a slot for new_dentry in the xarray

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment mentions "maple_tree" but the offset_ctx structure in this kernel version uses xarray (xa) not maple_tree. The comment should refer to "xarray" instead to match the actual data structure being used.

No.
The linux-stable maintenance policy requires that, when backporting patches, you keep them as close to the original commit as possible — even if the comments no longer match the modified code logic.

fs/libfs.c Outdated
ret = simple_offset_replace(new_ctx, old_dentry, new_index);
if (ret)
goto out_restore;
ret = mtree_store(&new_ctx->mt, new_index, old_dentry, GFP_KERNEL);
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code attempts to access new_ctx->mt but the offset_ctx structure only contains an xa (xarray) field, not an mt (maple_tree) field. This will cause a compilation error. The correct field to use is new_ctx->xa with xa_store instead of mtree_store. This appears to be a backport error where the upstream v6.19 code (which may use maple_tree) was not properly adapted for the 6.6-y kernel (which uses xarray).

Copilot uses AI. Check for mistakes.
fs/libfs.c Outdated
if (ret) {
simple_offset_remove(new_ctx, old_dentry);
goto out_restore;
ret = mtree_store(&old_ctx->mt, old_index, new_dentry, GFP_KERNEL);
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code attempts to access old_ctx->mt but the offset_ctx structure only contains an xa (xarray) field, not an mt (maple_tree) field. This will cause a compilation error. The correct field to use is old_ctx->xa with xa_store instead of mtree_store.

Copilot uses AI. Check for mistakes.
[ Upstream commit e1b4c6a58304fd490124cc2b454d80edc786665c ]

maple_tree insertions can fail if we are seriously short on memory;
simple_offset_rename() does not recover well if it runs into that.
The same goes for simple_offset_rename_exchange().

Moreover, shmem_whiteout() expects that if it succeeds, the caller will
progress to d_move(), i.e. that shmem_rename2() won't fail past the
successful call of shmem_whiteout().

Not hard to fix, fortunately - mtree_store() can't fail if the index we
are trying to store into is already present in the tree as a singleton.

For simple_offset_rename_exchange() that's enough - we just need to be
careful about the order of operations.

For simple_offset_rename() solution is to preinsert the target into the
tree for new_dir; the rest can be done without any potentially failing
operations.

That preinsertion has to be done in shmem_rename2() rather than in
simple_offset_rename() itself - otherwise we'd need to deal with the
possibility of failure after successful shmem_whiteout().

Fixes: a2e4595 ("shmem: stable directory offsets")
Reviewed-by: Christian Brauner <[email protected]>
Reviewed-by: Chuck Lever <[email protected]>
Signed-off-by: Al Viro <[email protected]>
[ Backport from v6.19 ]
[ Backport Adaptation Note:

The upstream kernel has migrated the struct offset_ctx from using
xarray to maple_tree, where the field is named mt and operations use
mtree_store(). However, the current 6.6-y kernel still uses the
original xarray-based implementation with the xa field and xa_store()
API.

This patch adapts the upstream commit by replacing the direct
mtree_store(&ctx->mt, ...) calls with the existing simple_offset_replace()
helper function, which internally performs xa_store(&octx->xa, ...). This
approach is logically equivalent to the original commit because:

 *1. Both mtree_store() and xa_store() serve the same purpose: storing a
dentry pointer at a given offset index in the directory's offset map.

  2. The simple_offset_replace() function already exists in the 6.6-y
codebase and correctly handles xarray operations including error checking
via xa_is_err()/xa_err().

  3. Since simple_offset_replace() also calls offset_set() internally, the
redundant offset_set() calls present after the store operations have been
removed to avoid setting the offset twice.

  4. The error handling and rollback logic remains identical: if the second
store fails, the first operation is reverted by restoring the original
dentry at its position. ]
Signed-off-by: WangYuli <[email protected]>
@deepin-ci-robot
Copy link

deepin pr auto review

我来对这个git diff进行详细审查:

  1. 代码逻辑改进:
  • simple_offset_rename 的返回类型从 int 改为 void,因为该函数现在只会在异常情况下通过 WARN_ON 报告错误,不再返回错误码
  • simple_offset_rename_exchange 中简化了错误处理逻辑,移除了复杂的回滚机制
  • shmem_rename2 中增加了对 offset 的预分配处理
  1. 代码质量改进:
  • 添加了更多的参数检查(WARN_ON)
  • 改进了错误处理流程,使其更加清晰
  • 在函数注释中添加了更详细的说明
  1. 代码性能改进:
  • 通过提前分配 offset 减少了后续操作的开销
  • 简化了错误处理路径,减少了不必要的操作
  1. 代码安全性改进:
  • 添加了更多的边界检查
  • 使用 WARN_ON 来捕获不应该发生的情况
  • 改进了资源清理机制

主要问题和建议:

  1. simple_offset_rename 中:
if (WARN_ON(!new_offset))
    return;

建议在返回前进行适当的清理工作,即使这是一个不应该发生的情况。

  1. simple_offset_rename_exchange 中:
if (WARN_ON(!old_index || !new_index))
    return -EINVAL;

这里的错误处理是合理的,但建议在返回前确保系统状态的一致性。

  1. shmem_rename2 中:
error = simple_offset_add(shmem_get_offset_ctx(new_dir), new_dentry);
if (error == -EBUSY)
    had_offset = true;
else if (unlikely(error))
    return error;

这个改动增加了对 offset 的预分配,但错误处理可能需要更细致的考虑,特别是在 EBUSY 的情况下。

  1. simple_offset_rename_exchange 中移除了 simple_rename_exchange 的错误检查:
simple_rename_exchange(old_dir, old_dentry, new_dir, new_dentry);
return 0;

这可能是一个潜在的问题,因为 simple_rename_exchange 可能会失败,建议保留错误检查。

总体建议:

  1. 在进行重大改动时,应该确保所有的错误路径都被正确处理
  2. 建议添加更多的注释来解释复杂的逻辑改动
  3. 考虑添加更多的单元测试来验证新的行为
  4. 对于关键操作,建议保留适当的错误检查和处理机制

这些改动整体上提高了代码的简洁性和可维护性,但需要注意确保错误处理的完整性。建议在合并前进行充分的测试,特别是在边界条件和错误情况下。

@opsiff opsiff merged commit 8747e2a into deepin-community:linux-6.6.y Dec 17, 2025
12 checks passed
@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: opsiff

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants