• Josef Bacik's avatar
    btrfs: do not resolve backrefs for roots that are being deleted · 39dba873
    Josef Bacik authored
    Zygo reported a deadlock where a task was stuck in the inode logical
    resolve code.  The deadlock looks like this
    
      Task 1
      btrfs_ioctl_logical_to_ino
      ->iterate_inodes_from_logical
       ->iterate_extent_inodes
        ->path->search_commit_root isn't set, so a transaction is started
          ->resolve_indirect_ref for a root that's being deleted
    	->search for our key, attempt to lock a node, DEADLOCK
    
      Task 2
      btrfs_drop_snapshot
      ->walk down to a leaf, lock it, walk up, lock node
       ->end transaction
        ->start transaction
          -> wait_cur_trans
    
      Task 3
      btrfs_commit_transaction
      ->wait_event(cur_trans->write_wait, num_writers == 1) DEADLOCK
    
    We are holding a transaction open in btrfs_ioctl_logical_to_ino while we
    try to resolve our references.  btrfs_drop_snapshot() holds onto its
    locks while it stops and starts transaction handles, because it assumes
    nobody is going to touch the root now.  Commit just does what commit
    does, waiting for the writers to finish, blocking any new trans handles
    from starting.
    
    Fix this by making the backref code not try to resolve backrefs of roots
    that are currently being deleted.  This will keep us from walking into a
    snapshot that's currently being deleted.
    
    This problem was harder to hit before because we rarely broke out of the
    snapshot delete halfway through, but with my delayed ref throttling code
    it happened much more often.  However we've always been able to do this,
    so it's not a new problem.
    
    Fixes: 8da6d581 ("Btrfs: added btrfs_find_all_roots()")
    Signed-off-by: default avatarJosef Bacik <josef@toxicpanda.com>
    Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
    39dba873
backref.c 60.2 KB