return ret;
 }
 
+/*
+ * The caller has joined a transaction or is holding a read lock on the
+ * fs_info->commit_root_sem semaphore, so no need to worry about the root's last
+ * snapshot field changing while updating or checking the cache.
+ */
+static bool lookup_backref_shared_cache(struct btrfs_backref_shared_cache *cache,
+                                       struct btrfs_root *root,
+                                       u64 bytenr, int level, bool *is_shared)
+{
+       struct btrfs_backref_shared_cache_entry *entry;
+
+       if (WARN_ON_ONCE(level >= BTRFS_MAX_LEVEL))
+               return false;
+
+       /*
+        * Level -1 is used for the data extent, which is not reliable to cache
+        * because its reference count can increase or decrease without us
+        * realizing. We cache results only for extent buffers that lead from
+        * the root node down to the leaf with the file extent item.
+        */
+       ASSERT(level >= 0);
+
+       entry = &cache->entries[level];
+
+       /* Unused cache entry or being used for some other extent buffer. */
+       if (entry->bytenr != bytenr)
+               return false;
+
+       /*
+        * We cached a false result, but the last snapshot generation of the
+        * root changed, so we now have a snapshot. Don't trust the result.
+        */
+       if (!entry->is_shared &&
+           entry->gen != btrfs_root_last_snapshot(&root->root_item))
+               return false;
+
+       /*
+        * If we cached a true result and the last generation used for dropping
+        * a root changed, we can not trust the result, because the dropped root
+        * could be a snapshot sharing this extent buffer.
+        */
+       if (entry->is_shared &&
+           entry->gen != btrfs_get_last_root_drop_gen(root->fs_info))
+               return false;
+
+       *is_shared = entry->is_shared;
+
+       return true;
+}
+
+/*
+ * The caller has joined a transaction or is holding a read lock on the
+ * fs_info->commit_root_sem semaphore, so no need to worry about the root's last
+ * snapshot field changing while updating or checking the cache.
+ */
+static void store_backref_shared_cache(struct btrfs_backref_shared_cache *cache,
+                                      struct btrfs_root *root,
+                                      u64 bytenr, int level, bool is_shared)
+{
+       struct btrfs_backref_shared_cache_entry *entry;
+       u64 gen;
+
+       if (WARN_ON_ONCE(level >= BTRFS_MAX_LEVEL))
+               return;
+
+       /*
+        * Level -1 is used for the data extent, which is not reliable to cache
+        * because its reference count can increase or decrease without us
+        * realizing. We cache results only for extent buffers that lead from
+        * the root node down to the leaf with the file extent item.
+        */
+       ASSERT(level >= 0);
+
+       if (is_shared)
+               gen = btrfs_get_last_root_drop_gen(root->fs_info);
+       else
+               gen = btrfs_root_last_snapshot(&root->root_item);
+
+       entry = &cache->entries[level];
+       entry->bytenr = bytenr;
+       entry->is_shared = is_shared;
+       entry->gen = gen;
+
+       /*
+        * If we found an extent buffer is shared, set the cache result for all
+        * extent buffers below it to true. As nodes in the path are COWed,
+        * their sharedness is moved to their children, and if a leaf is COWed,
+        * then the sharedness of a data extent becomes direct, the refcount of
+        * data extent is increased in the extent item at the extent tree.
+        */
+       if (is_shared) {
+               for (int i = 0; i < level; i++) {
+                       entry = &cache->entries[i];
+                       entry->is_shared = is_shared;
+                       entry->gen = gen;
+               }
+       }
+}
+
 /*
  * Check if a data extent is shared or not.
  *
  * @bytenr: logical bytenr of the extent we are checking
  * @roots:  list of roots this extent is shared among
  * @tmp:    temporary list used for iteration
+ * @cache:  a backref lookup result cache
  *
  * btrfs_is_data_extent_shared uses the backref walking code but will short
  * circuit as soon as it finds a root or inode that doesn't match the
  * Return: 0 if extent is not shared, 1 if it is shared, < 0 on error.
  */
 int btrfs_is_data_extent_shared(struct btrfs_root *root, u64 inum, u64 bytenr,
-                               struct ulist *roots, struct ulist *tmp)
+                               struct ulist *roots, struct ulist *tmp,
+                               struct btrfs_backref_shared_cache *cache)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
        struct btrfs_trans_handle *trans;
                .inum = inum,
                .share_count = 0,
        };
+       int level;
 
        ulist_init(roots);
        ulist_init(tmp);
                btrfs_get_tree_mod_seq(fs_info, &elem);
        }
 
+       /* -1 means we are in the bytenr of the data extent. */
+       level = -1;
        ULIST_ITER_INIT(&uiter);
        while (1) {
+               bool is_shared;
+               bool cached;
+
                ret = find_parent_nodes(trans, fs_info, bytenr, elem.seq, tmp,
                                        roots, NULL, &shared, false);
                if (ret == BACKREF_FOUND_SHARED) {
                        /* this is the only condition under which we return 1 */
                        ret = 1;
+                       if (level >= 0)
+                               store_backref_shared_cache(cache, root, bytenr,
+                                                          level, true);
                        break;
                }
                if (ret < 0 && ret != -ENOENT)
                        break;
                ret = 0;
+               if (level >= 0)
+                       store_backref_shared_cache(cache, root, bytenr,
+                                                  level, false);
                node = ulist_next(tmp, &uiter);
                if (!node)
                        break;
                bytenr = node->val;
+               level++;
+               cached = lookup_backref_shared_cache(cache, root, bytenr, level,
+                                                    &is_shared);
+               if (cached) {
+                       ret = (is_shared ? 1 : 0);
+                       break;
+               }
                shared.share_count = 0;
                cond_resched();
        }