*/
 #define SEND_MAX_DIR_CREATED_CACHE_SIZE                        64
 
+/*
+ * Max number of entries in the cache that stores directories that were already
+ * created. The cache uses raw struct btrfs_lru_cache_entry entries, so it uses
+ * at most 4096 bytes - sizeof(struct btrfs_lru_cache_entry) is 48 bytes, but
+ * the kmalloc-64 slab is used, so we get 4096 bytes (64 bytes * 64).
+ */
+#define SEND_MAX_DIR_UTIMES_CACHE_SIZE                 64
+
 struct send_ctx {
        struct file *send_filp;
        loff_t send_off;
        u64 backref_cache_last_reloc_trans;
 
        struct btrfs_lru_cache dir_created_cache;
+       struct btrfs_lru_cache dir_utimes_cache;
 };
 
 struct pending_dir_move {
        return ret;
 }
 
+/*
+ * If the cache is full, we can't remove entries from it and do a call to
+ * send_utimes() for each respective inode, because we might be finishing
+ * processing an inode that is a directory and it just got renamed, and existing
+ * entries in the cache may refer to inodes that have the directory in their
+ * full path - in which case we would generate outdated paths (pre-rename)
+ * for the inodes that the cache entries point to. Instead of prunning the
+ * cache when inserting, do it after we finish processing each inode at
+ * finish_inode_if_needed().
+ */
+static int cache_dir_utimes(struct send_ctx *sctx, u64 dir, u64 gen)
+{
+       struct btrfs_lru_cache_entry *entry;
+       int ret;
+
+       entry = btrfs_lru_cache_lookup(&sctx->dir_utimes_cache, dir, gen);
+       if (entry != NULL)
+               return 0;
+
+       /* Caching is optional, don't fail if we can't allocate memory. */
+       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry)
+               return send_utimes(sctx, dir, gen);
+
+       entry->key = dir;
+       entry->gen = gen;
+
+       ret = btrfs_lru_cache_store(&sctx->dir_utimes_cache, entry, GFP_KERNEL);
+       ASSERT(ret != -EEXIST);
+       if (ret) {
+               kfree(entry);
+               return send_utimes(sctx, dir, gen);
+       }
+
+       return 0;
+}
+
+static int trim_dir_utimes_cache(struct send_ctx *sctx)
+{
+       while (btrfs_lru_cache_size(&sctx->dir_utimes_cache) >
+              SEND_MAX_DIR_UTIMES_CACHE_SIZE) {
+               struct btrfs_lru_cache_entry *lru;
+               int ret;
+
+               lru = btrfs_lru_cache_lru_entry(&sctx->dir_utimes_cache);
+               ASSERT(lru != NULL);
+
+               ret = send_utimes(sctx, lru->key, lru->gen);
+               if (ret)
+                       return ret;
+
+               btrfs_lru_cache_remove(&sctx->dir_utimes_cache, lru);
+       }
+
+       return 0;
+}
+
 /*
  * Sends a BTRFS_SEND_C_MKXXX or SYMLINK command to user space. We don't have
  * a valid path yet because we did not process the refs yet. So, the inode
        }
 
 finish:
-       ret = send_utimes(sctx, pm->ino, pm->gen);
+       ret = cache_dir_utimes(sctx, pm->ino, pm->gen);
        if (ret < 0)
                goto out;
 
                if (ret < 0)
                        goto out;
 
-               ret = send_utimes(sctx, cur->dir, cur->dir_gen);
+               ret = cache_dir_utimes(sctx, cur->dir, cur->dir_gen);
                if (ret < 0)
                        goto out;
        }
 
                if (ret == inode_state_did_create ||
                    ret == inode_state_no_change) {
-                       /* TODO delayed utimes */
-                       ret = send_utimes(sctx, cur->dir, cur->dir_gen);
+                       ret = cache_dir_utimes(sctx, cur->dir, cur->dir_gen);
                        if (ret < 0)
                                goto out;
                } else if (ret == inode_state_did_delete &&
                 * it's moved/renamed, therefore we don't need to do it here.
                 */
                sctx->send_progress = sctx->cur_ino + 1;
-               ret = send_utimes(sctx, sctx->cur_ino, sctx->cur_inode_gen);
+
+               /*
+                * If the current inode is a non-empty directory, delay issuing
+                * the utimes command for it, as it's very likely we have inodes
+                * with an higher number inside it. We want to issue the utimes
+                * command only after adding all dentries to it.
+                */
+               if (S_ISDIR(sctx->cur_inode_mode) && sctx->cur_inode_size > 0)
+                       ret = cache_dir_utimes(sctx, sctx->cur_ino, sctx->cur_inode_gen);
+               else
+                       ret = send_utimes(sctx, sctx->cur_ino, sctx->cur_inode_gen);
+
                if (ret < 0)
                        goto out;
        }
 
 out:
+       if (!ret)
+               ret = trim_dir_utimes_cache(sctx);
+
        return ret;
 }
 
        int clone_sources_to_rollback = 0;
        size_t alloc_size;
        int sort_clone_roots = 0;
+       struct btrfs_lru_cache_entry *entry;
+       struct btrfs_lru_cache_entry *tmp;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
        btrfs_lru_cache_init(&sctx->backref_cache, SEND_MAX_BACKREF_CACHE_SIZE);
        btrfs_lru_cache_init(&sctx->dir_created_cache,
                             SEND_MAX_DIR_CREATED_CACHE_SIZE);
+       /*
+        * This cache is periodically trimmed to a fixed size elsewhere, see
+        * cache_dir_utimes() and trim_dir_utimes_cache().
+        */
+       btrfs_lru_cache_init(&sctx->dir_utimes_cache, 0);
 
        sctx->pending_dir_moves = RB_ROOT;
        sctx->waiting_dir_moves = RB_ROOT;
        if (ret < 0)
                goto out;
 
+       btrfs_lru_cache_for_each_entry_safe(&sctx->dir_utimes_cache, entry, tmp) {
+               ret = send_utimes(sctx, entry->key, entry->gen);
+               if (ret < 0)
+                       goto out;
+               btrfs_lru_cache_remove(&sctx->dir_utimes_cache, entry);
+       }
+
        if (!(sctx->flags & BTRFS_SEND_FLAG_OMIT_END_CMD)) {
                ret = begin_cmd(sctx, BTRFS_SEND_C_END);
                if (ret < 0)
                btrfs_lru_cache_clear(&sctx->name_cache);
                btrfs_lru_cache_clear(&sctx->backref_cache);
                btrfs_lru_cache_clear(&sctx->dir_created_cache);
+               btrfs_lru_cache_clear(&sctx->dir_utimes_cache);
 
                kfree(sctx);
        }