Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dbuf: separate refcount calls for dbuf and dbuf_user #16191

Merged
merged 1 commit into from
May 15, 2024

Conversation

robn
Copy link
Member

@robn robn commented May 11, 2024

Motivation and Context

Investigation on #15802 found this. This is probably not the fix for that, but it is in the way of hunting it down.

Description

In 92dc4ad I updated the dbuf_cache accounting to track the size of userdata associated with dbufs. This adds the size of the dbuf+userdata together in a single call to zfs_refcount_add_many(), but sometime removes them in separate calls to zfs_refcount_remove_many(), if dbuf and userdata are evicted separately.

What I didn't realise is that when refcount tracking is on, zfs_refcount_add_many() and zfs_refcount_remove_many() are expected to be paired, with their second & third args (count & holder) the same on both sides. Splitting the remove part into two calls means the counts don't match up, tripping a panic.

This commit fixes that, by always adding and removing the dbuf and userdata counts separately.

How Has This Been Tested?

Just light sanity checking Test suite run with debug enabled is in progress.

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:

@markjdb
Copy link
Contributor

markjdb commented May 11, 2024

If I boot the affected system with this patch, I get the panic below. Though, this is the result of patching FreeBSD's in-tree copy of OpenZFS, which might be different enough from master to cause problems?

panic: No such hold 0xffffa7ffd1782d80 on refcount ffff0000017a7d80                                                                                                                                                                                                                                                           
cpuid = 35                                                                                                                                                                                                                                                                                                                    
time = 9                                                                                                                                                                                                                                                                                                                      
KDB: stack backtrace:                                                                                                                                                                                                                                                                                                         
db_trace_self() at db_trace_self                                                                                                                                                                                                                                                                                              
db_trace_self_wrapper() at db_trace_self_wrapper+0x38                                                                                                                                                                                                                                                                         
vpanic() at vpanic+0x1ac                                                                                                                                                                                                                                                                                                      
panic() at panic+0x48                                                                                                                                                                                                                                                                                                         
zfs_refcount_remove_many() at zfs_refcount_remove_many+0x1e8                                                                                                                                                                                                                                                                  
dbuf_hold_impl() at dbuf_hold_impl+0x520                                                                                                                                                                                                                                                                                      
dbuf_hold() at dbuf_hold+0x28                                                                                                                                                                                                                                                                                                 
dmu_buf_hold_noread_by_dnode() at dmu_buf_hold_noread_by_dnode+0x54                                                                                                                                                                                                                                                           
dmu_buf_hold_by_dnode() at dmu_buf_hold_by_dnode+0x20                                                                                                                                                                                                                                                                         
zap_lockdir() at zap_lockdir+0x58                                                                                                                                                                                                                                                                                             
zap_lookup_norm() at zap_lookup_norm+0x54                                                                                                                                                                                                                                                                                     
zap_lookup() at zap_lookup+0x20                                                                                                                                                                                                                                                                                               
spa_load() at spa_load+0x150                                                                                                                                                                                                                                                                                                  
spa_load_best() at spa_load_best+0x70                                                                                                                                                                                                                                                                                         
spa_open_common() at spa_open_common+0x14c                                                                                                                                                                                                                                                                                    
dsl_pool_hold() at dsl_pool_hold+0x28                                                                                                                                                                                                                                                                                         
dmu_objset_own() at dmu_objset_own+0x4c                                                                                                                                                                                                                                                                                       
zfsvfs_create() at zfsvfs_create+0xb4                                                                                                                                                                                                                                                                                         
$x.1() at $x.1+0x2d4                                                                                                                                                                                                                                                                                                          
vfs_domount_first() at vfs_domount_first+0x23c                                                                                                                                                                                                                                                                                
vfs_domount() at vfs_domount+0x2d4                                                                                                                                                                                                                                                                                            
vfs_donmount() at vfs_donmount+0x844                                                                                                                                                                                                                                                                                          
kernel_mount() at kernel_mount+0x64                                                                                                                                                                                                                                                                                           
parse_mount() at parse_mount+0x4a0                                                                                                                                                                                                                                                                                            
vfs_mountroot() at vfs_mountroot+0x5ac                                                                                                                                                                                                                                                                                        
start_init() at start_init+0x2c                                                                                                                                                                                                                                                                                               
fork_exit() at fork_exit+0x78                                                                                                                                                                                                                                                                                                 
fork_trampoline() at fork_trampoline+0x18                                                                                                                                                                                                                                                                                     
KDB: enter: panic                                                                                                                                                                                                                                                                                                             
[ thread pid 1 tid 100002 ]                                                                                                                                                                                                                                                                                                   
Stopped at      kdb_enter+0x48: str     xzr, [x19, #2048]

In 92dc4ad I updated the dbuf_cache accounting to track the size of
userdata associated with dbufs. This adds the size of the dbuf+userdata
together in a single call to zfs_refcount_add_many(), but sometime
removes them in separate calls to zfs_refcount_remove_many(), if dbuf
and userdata are evicted separately.

What I didn't realise is that when refcount tracking is on,
zfs_refcount_add_many() and zfs_refcount_remove_many() are expected to
be paired, with their second & third args (count & holder) the same on
both sides. Splitting the remove part into two calls means the counts
don't match up, tripping a panic.

This commit fixes that, by always adding and removing the dbuf and
userdata counts separately.

Sponsored-by: Klara, Inc.
Sponsored-by: Wasabi Technology, Inc.
Reported-by: Mark Johnston <markj@FreeBSD.org>
Signed-off-by: Rob Norris <rob.norris@klarasystems.com>
@robn robn force-pushed the dbuf-userdata-refcount-separate branch from f8f576e to 9341a9e Compare May 11, 2024 21:46
@robn
Copy link
Member Author

robn commented May 11, 2024

Ugh, that's my mistake - overexcited copypasta. Updated.

Re in-tree, it'll be the same in main, but not in 14; specifically, after freebsd/freebsd-src@a2b560cc. It wouldn't apply cleanly without that, so I reckon you're good.

@markjdb
Copy link
Contributor

markjdb commented May 12, 2024

Ugh, that's my mistake - overexcited copypasta. Updated.

The new version appears to fix the problem for me, thanks! As expected, the ARC leak is still there.

@behlendorf behlendorf added the Status: Accepted Ready to integrate (reviewed, tested) label May 13, 2024
@behlendorf behlendorf merged commit e675852 into openzfs:master May 15, 2024
22 of 24 checks passed
lundman pushed a commit to openzfsonwindows/openzfs that referenced this pull request Sep 4, 2024
In 92dc4ad I updated the dbuf_cache accounting to track the size of
userdata associated with dbufs. This adds the size of the dbuf+userdata
together in a single call to zfs_refcount_add_many(), but sometime
removes them in separate calls to zfs_refcount_remove_many(), if dbuf
and userdata are evicted separately.

What I didn't realise is that when refcount tracking is on,
zfs_refcount_add_many() and zfs_refcount_remove_many() are expected to
be paired, with their second & third args (count & holder) the same on
both sides. Splitting the remove part into two calls means the counts
don't match up, tripping a panic.

This commit fixes that, by always adding and removing the dbuf and
userdata counts separately.

Sponsored-by: Klara, Inc.
Sponsored-by: Wasabi Technology, Inc.
Reported-by: Mark Johnston <markj@FreeBSD.org>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Signed-off-by: Rob Norris <rob.norris@klarasystems.com>
Closes openzfs#16191
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Accepted Ready to integrate (reviewed, tested)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants