Skip to content

range_tree: Add zfs_recover_rt parameter and extra debug info#17094

Open
ihoro wants to merge 1 commit into
openzfs:masterfrom
KlaraSystems:zfs_recover_ms
Open

range_tree: Add zfs_recover_rt parameter and extra debug info#17094
ihoro wants to merge 1 commit into
openzfs:masterfrom
KlaraSystems:zfs_recover_ms

Conversation

@ihoro

@ihoro ihoro commented Feb 25, 2025

Copy link
Copy Markdown
Contributor

Motivation and Context

There are production cases when loading of a metaslab leads to a ZFS panic due to unexpected entries in its spacemap (presumably). The assertions in zfs_range_tree_add_impl() and zfs_range_tree_remove_impl() fail due to overlapping or missing segments, etc. A business would like to go ahead with such pools while the root cause is being investigated.

Description

The idea is to allow loading such metaslabs with a potential space leak as a trade-off instead of a potential data loss.

We already have zfs_recover module parameter to mitigate various issues, including some range tree cases, and this patch adds zfs_recover_ms parameter to localize the recovery behavior to the metaslab loading process only.

The following diagrams are expected to help with the details:

zfs_recover_ms

How Has This Been Tested?

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Performance enhancement (non-breaking change which improves efficiency)
  • Code cleanup (non-breaking change which makes code smaller or more readable)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Library ABI change (libzfs, libzfs_core, libnvpair, libuutil and libzfsbootenv)
  • Documentation (a change to man pages or other documentation)

Checklist:

@github-actions github-actions Bot added the Status: Work in Progress Not yet ready for general review label Feb 25, 2025
Comment thread module/zfs/spa_misc.c Outdated
Comment thread module/zfs/range_tree.c Outdated
@ihoro ihoro changed the title Add zfs_recover_ms parameter range_tree: Add zfs_recover_rt parameter and extra debug info Mar 4, 2025
@ihoro

ihoro commented Mar 4, 2025

Copy link
Copy Markdown
Contributor Author

The update includes:

  • Re-work into zfs_recover_rt parameter instead of zfs_recover_ms. It covers all range trees.
  • Add range tree name as an extra debug info resulting into a message like zfs: rt_instance=vdev_obsolete_segments: ....
  • The metaslab related trees provide even more details like zfs: rt_instance={spa=p1 vdev_guid=4127788562752866619 ms_id=0 ms_allocatable}: ....
  • Update zfs.4 man page respectively

@ihoro ihoro marked this pull request as ready for review March 4, 2025 13:12
@github-actions github-actions Bot added Status: Code Review Needed Ready for review and testing and removed Status: Work in Progress Not yet ready for general review labels Mar 4, 2025
@tonyhutter

Copy link
Copy Markdown
Contributor

Looks like there's a build issue:

    CC       module/zfs/libzpool_la-dsl_bookmark.lo
    CC       module/zfs/libzpool_la-dsl_crypt.lo
  module/zfs/dnode.c: In function 'dnode_free_range':
  module/zfs/dnode.c:2441:59: error: passing argument 7 of 'zfs_range_tree_create_usecase' discards 'const' qualifier from pointer target type [-Werror=discarded-qualifiers]
   2441 |                             ZFS_RANGE_TREE_UC_FREE_SPACE, "dn_free_ranges");
        |                                                           ^~~~~~~~~~~~~~~~
  In file included from ./include/sys/space_map.h:34,
                   from ./include/sys/spa.h:46,
                   from ./include/sys/dbuf.h:32,
                   from module/zfs/dnode.c:28:
  ./include/sys/range_tree.h:294:45: note: expected 'char *' but argument is of type 'const char *'
    294 |     zfs_range_tree_usecase_t usecase, char *instance);
        |                                       ~~~~~~^~~~~~~~
    CC       module/zfs/libzpool_la-dsl_dataset.lo
    CC       module/zfs/libzpool_la-dsl_deadlist.lo
  cc1: all warnings being treated as errors
  make[4]: *** [Makefile:10313: module/zfs/libzpool_la-dnode.lo] Error 1

@ihoro

ihoro commented Mar 7, 2025

Copy link
Copy Markdown
Contributor Author

Looks like there's a build issue:

Indeed, there should have been one more dev iteration on my side. It should be fixed now.

@tonyhutter

Copy link
Copy Markdown
Contributor

For some reason a bunch of the runners are failing. I'm going to manually restart then.

@tonyhutter

Copy link
Copy Markdown
Contributor

Please rebase on master; that will pull in the new .github/workflow/* files and allow the tests to complete.

@ihoro

ihoro commented Mar 11, 2025

Copy link
Copy Markdown
Contributor Author

For some reason a bunch of the runners are failing. I'm going to manually restart then.

Thank you.

Please rebase on master; that will pull in the new .github/workflow/* files and allow the tests to complete.

Sure, it's been moved 3 commits up, over the recent workflow changes, it should work now.

Comment thread include/sys/range_tree.h Outdated
Comment thread module/zfs/range_tree.c Outdated

@amotin amotin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see plenty of places where use case is not defined. We could look better. Also I think in many cases we could still log pool and vdev, even if we have no metaslab number to report.

Comment thread module/zfs/metaslab.c Outdated
Comment thread include/sys/range_tree.h Outdated
Comment thread include/sys/range_tree.h Outdated
There are production cases where unexpected range tree segment
adding/removal leads to panic. The root cause investigation requires
more debug info about the range tree and the segments in question when
it happens. In addition, the zfs_recover_rt parameter allows converting
such panics into warnings with a potential space leak as a trade-off.

Signed-off-by: Igor Ostapenko <igor.ostapenko@klarasystems.com>
@ihoro

ihoro commented Mar 13, 2025

Copy link
Copy Markdown
Contributor Author

I see plenty of places where use case is not defined. We could look better.

It seems it's time to rename the _UC_UNKNOWN flag to the more appropriate one _UC_GENERIC. It depicts the intention better -- a range tree without special treatment where zfs_recover_rt simply does not panic upon unexpected additions/removal.

The unknown (now generic) flag is intentionally used for the range tree instances where special treatment is not expected. Sometimes it's not about allocated/free space or it's a temporary tree which is based on already "recovered" other ones. Anyway, I think I could review the instances once again, probably someone should not be GENERIC.

Also I think in many cases we could still log pool and vdev, even if we have no metaslab number to report.

Yes, it's worth the extra code to be maximum useful. It will come with the next iteration of the patch.

Comment thread include/sys/range_tree.h
* name string, which can be marked as dynamic to be freed along with the tree
* instance destruction.
*/
#define ZFS_RANGE_TREE_F_UC_GENERIC (1 << 0)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think "GENERIC" is really meaningful. Easier and cleaner I think would be just to pass 0 if we can't say anything better (we really should).

@GregorKopka

Copy link
Copy Markdown
Contributor

I disagree on logic outlined the diagrams, regardless if it can already be triggered by some module parameter:
Freeing something already free is something completely different than allocating something that is already in use.

It is IMHO a very bad idea to allow the latter to happen.

Comment thread module/zfs/range_tree.c
if (delta < 0 && delta * -1 >= zfs_rs_get_fill(rs, rt)) {
zfs_panic_recover("zfs: attempting to decrease fill to or "
"below 0; probable double remove in segment [%llx:%llx]",
zfs_panic_recover_rt("zfs: rt_instance=%s: attempting to "

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Cosmetics, but here and in other places I would reduce "rt_instance=" to "rt=", since it provides no useful information, and the line is too long. Or otherwise write full "range tree" to make it more human-readable if we don't care about length.

@amotin

amotin commented Jun 19, 2025

Copy link
Copy Markdown
Member

@ihoro So how about remove unneeded ZFS_RANGE_TREE_F_UC_GENERIC constant as I have asked and one more comment above? I don't really believe in the "recovery" part, but I do like more informative panic messages.

@amotin

amotin commented Jul 25, 2025

Copy link
Copy Markdown
Member

@ihoro ping?

@ihoro

ihoro commented Jul 30, 2025

Copy link
Copy Markdown
Contributor Author

The debug info part has been extracted into a separate PR: #17581

@amotin

amotin commented Aug 5, 2025

Copy link
Copy Markdown
Member

This needs a rebase after #17581 has beed merged.

@behlendorf behlendorf self-requested a review August 6, 2025 19:43
@behlendorf behlendorf added the Status: Revision Needed Changes are required for the PR to be accepted label Aug 7, 2025
@behlendorf

Copy link
Copy Markdown
Contributor

@ihoro can you rebase this now that #17581 has been merged.

@ihoro

ihoro commented Oct 5, 2025

Copy link
Copy Markdown
Contributor Author

I've got back into the context of this and there is a desire to revise the interface and expectations from the end user perspective. I think we could discuss the following topics:

  • First of all, it reminds me that this is not a fix. If a pool has overlapping issue then there is a chance it is already in trouble, i.e., some data is lost. There might be reasons why we want to make it running anyway, but users should understand that ignoring such troubles opens undefined behavior path and potentially leads to more issues on top of the existing ones.
  • The above means to rework the man page to avoid wording like "to recover from fatal issues" etc. The old patch simply follows the existing zfs_recover param's concept.
  • There is a strong intuition that this behavior must be detached from the existing zfs_recover. The old patch extends the area which zfs_recover param covers and allows to activate only part of it by using zfs_recover_rt. What do you think if we keep zfs_recover as is and simply add a new set of behavior activated separately? And, obviously, it should not have any recover in the name not to mislead compared to the existing zfs_recover.
  • Also, there is an intuition that the interface should provide a way to ignore such issues only during metaslab loading and still panic upon all other cases. Otherwise, ignoring all range tree overlapping issues touches all aspects of ZFS, not only related to the allocation accounting.
  • And we can provide the "full mode" to ignore all range tree issues.

The above is a conceptual discussion of what we would like to have. If we go down the technical road then we could discuss naming options, whether we want to use bool-like knobs or bitfield ones, whether it should collaborate with the existing metaslab_debug_load parameter instead of introducing a brand new one, and so on.

@amotin

amotin commented Oct 6, 2025

Copy link
Copy Markdown
Member

ignore such issues only during metaslab loading and still panic upon all other cases

This makes some sense to me. Duplicate frees found during loading has already happened and are already on disk, and didn't lead anywhere so far. Would it happen earlier (in other places), it could be more informative.

@behlendorf

Copy link
Copy Markdown
Contributor

ignore such issues only during metaslab loading and still panic upon all other cases

To build on this a little bit, what I think we want is to add a metaslab_skip_unloadable tunable. When set, if any issues are encountered when loading a metaslab from disk (like duplicate frees) then; 1) log an error to the debug log, 2) abort the metaslab load, 3) mark the metaslab as damaged in memory so we don't try it again, and 4) try loading a different metaslab. This would at least allow the pool to be imported read/write without risking further damage to that metaslab. In all other cases I agree we need to panic.

@GregorKopka

GregorKopka commented Oct 7, 2025

Copy link
Copy Markdown
Contributor

In all other cases I agree we need to panic.

Would it be feasible to 'panic' just the affected pool, preferably in a way that allows to (force) unload anything related to it without needing a (hard) reboot... instead halting the whole system?

@amotin

amotin commented Oct 7, 2025

Copy link
Copy Markdown
Member

Would it be feasible to 'panic' just the affected pool

Would we be able to handle it as Brian described, we would not need to panic at all.

@wrobelda

wrobelda commented Dec 30, 2025

Copy link
Copy Markdown

Is there any update on this? I just run into space corruption issue on my homelab server with zfs_range_tree_add_impl() panicking on boot:
#18094

Happy to use the pool in its current state to test these changes and hopefully helping recover from the problem.

@wrobelda

wrobelda commented Jan 3, 2026

Copy link
Copy Markdown

@ihoro FYI, I managed to use this PR to recover my pool. Thanks a lot for your work!

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

Labels

Status: Code Review Needed Ready for review and testing Status: Revision Needed Changes are required for the PR to be accepted

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants