From e794ff245a0e46a8f1b888f4ed3cb26324d21d19 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 16:07:18 -0400 Subject: [PATCH 01/20] actiondfs: check published directory metadata before node origins --- kernel/actiondfs/actiondfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index aa67698..cd17852 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -2183,8 +2183,8 @@ static noinline_for_stack int actiondfs_load_dir(struct super_block *sb, static __always_inline int actiondfs_ensure_loaded(struct super_block *sb, struct actiondfs_node *dir) { - if (dir->origin == ACTIONDFS_NODE_STAGED || - smp_load_acquire(&dir->cached_dir)) + if (smp_load_acquire(&dir->cached_dir) || + dir->origin == ACTIONDFS_NODE_STAGED) return 0; return actiondfs_load_dir(sb, dir); } From d6acbd351555312a117fc98345a94a7de9fcc982 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 16:07:47 -0400 Subject: [PATCH 02/20] actiondfs: trust staged backing files during write completion --- kernel/actiondfs/actiondfs.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index cd17852..d58843f 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -1317,9 +1317,6 @@ static void actiondfs_stage_end_write(struct kiocb *iocb, ssize_t written) return; backing_file = iocb->ki_filp->private_data; - if (!backing_file) - return; - actiondfs_sync_staged_inode(file_inode(iocb->ki_filp), file_inode(backing_file)); actiondfs_stat_add(ACTIONDFS_STAT_STAGE_WRITE_BYTES, (u64)written); From 2cb100682b8fb3bae917080d7eeef498466de4fd Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 16:09:41 -0400 Subject: [PATCH 03/20] actiondfs: splice backing files without redundant credential swaps --- kernel/actiondfs/actiondfs.c | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index d58843f..a68d77b 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -1457,26 +1457,18 @@ static ssize_t actiondfs_splice_read(struct file *actiondfs_file, loff_t *ppos, struct inode *inode = file_inode(actiondfs_file); struct actiondfs_node *node = inode->i_private; struct file *file; - struct kiocb backing_iocb; loff_t pos = *ppos; size_t wanted; ssize_t nread; - struct backing_file_ctx ctx = { - .cred = current_cred(), - }; if (node->origin == ACTIONDFS_NODE_STAGED) { - struct kiocb backing_iocb; u64 total_start = actiondfs_stat_time_start(); actiondfs_stat_inc(ACTIONDFS_STAT_STAGE_SPLICE_READ_CALLS); file = actiondfs_file->private_data; - init_sync_kiocb(&backing_iocb, actiondfs_file); - backing_iocb.ki_pos = pos; - nread = backing_file_splice_read(file, &backing_iocb, pipe, - len, flags, &ctx); + nread = vfs_splice_read(file, &pos, pipe, len, flags); if (nread > 0) { - *ppos = backing_iocb.ki_pos; + *ppos = pos; actiondfs_stat_add(ACTIONDFS_STAT_STAGE_SPLICE_READ_BYTES, (u64)nread); } @@ -1498,14 +1490,8 @@ static ssize_t actiondfs_splice_read(struct file *actiondfs_file, loff_t *ppos, if (IS_ERR(file)) return PTR_ERR(file); - init_sync_kiocb(&backing_iocb, actiondfs_file); - backing_iocb.ki_pos = pos; actiondfs_stat_inc(ACTIONDFS_STAT_SPLICE_READS); - nread = backing_file_splice_read(file, &backing_iocb, pipe, wanted, - flags, &ctx); - if (nread > 0) - pos = backing_iocb.ki_pos; - + nread = vfs_splice_read(file, &pos, pipe, wanted, flags); if (nread > 0) { *ppos = pos; actiondfs_stat_add(ACTIONDFS_STAT_SPLICE_READ_BYTES, (u64)nread); From 40da3df2cbf54c96fe621a4bc218bed8936eb505 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 16:11:24 -0400 Subject: [PATCH 04/20] actiondfs: omit file accounting for internal directory blobs --- kernel/actiondfs/actiondfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index a68d77b..e6602be 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -1092,8 +1092,8 @@ static struct file *actiondfs_open_directory_blob(struct actiondfs_sb_info *sbi, if (IS_ERR(real_path.dentry)) { file = ERR_CAST(real_path.dentry); } else { - file = dentry_open(&real_path, O_RDONLY | O_NONBLOCK, - current_cred()); + file = kernel_file_open(&real_path, O_RDONLY | O_NONBLOCK, + current_cred()); dput(real_path.dentry); } if (!IS_ERR(file)) From 3a4aa09a5ee27535c4c97c12599d2a1dfa7e1393 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 16:11:46 -0400 Subject: [PATCH 05/20] actiondfs: initialize staged inodes once after backing creation --- kernel/actiondfs/actiondfs.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index e6602be..b3ad1b0 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -2315,7 +2315,7 @@ static struct inode *actiondfs_prealloc_staged_inode( actiondfs_free_node(node); return ERR_PTR(-ENOMEM); } - actiondfs_init_inode(inode, node); + inode->i_private = node; return inode; } @@ -2329,9 +2329,7 @@ static void actiondfs_insert_staged_inode(struct inode *inode, node->mode = (node->mode & S_IFMT) | (real_inode->i_mode & S_IALLUGO); node->stage_dentry = real_dentry; - inode->i_ino = node->ino; - inode->i_mode = node->mode; - actiondfs_copy_stage_owner(inode, real_inode); + actiondfs_init_inode(inode, node); insert_inode_hash(inode); } From 840759b875507affc79f40c333e49ac61151cd18 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 16:13:09 -0400 Subject: [PATCH 06/20] actiondfs: trust validated immutable backing inode types --- kernel/actiondfs/actiondfs.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index b3ad1b0..119b65b 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -1116,7 +1116,6 @@ static struct file *actiondfs_open_backing_cas_blob(struct actiondfs_sb_info *sb struct file *file; struct inode *real_inode; struct path real_path; - loff_t real_size; u64 total_start = actiondfs_stat_time_start(); int err; @@ -1137,9 +1136,7 @@ static struct file *actiondfs_open_backing_cas_blob(struct actiondfs_sb_info *sb } real_inode = d_inode(real_path.dentry); - real_size = real_inode ? i_size_read(real_inode) : -1; - if (!real_inode || !S_ISREG(real_inode->i_mode) || - (u64)real_size != expected_size) { + if ((u64)i_size_read(real_inode) != expected_size) { dput(real_path.dentry); file = ERR_PTR(-EIO); break; From 131bab3e56587ec2f77dcbfa59dec2ee074bc7e6 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 16:14:20 -0400 Subject: [PATCH 07/20] actiondfs: publish canonical cached directories without atomic exchange --- kernel/actiondfs/actiondfs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index 119b65b..f75359f 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -2155,8 +2155,12 @@ static noinline_for_stack int actiondfs_load_dir(struct super_block *sb, err = actiondfs_get_cached_dir(sbi, hash, dir->size, &cached); if (err) return err; +#if ACTIONDFS_ENABLE_STATS if (!cmpxchg_release(&dir->cached_dir, NULL, cached)) actiondfs_stat_inc(ACTIONDFS_STAT_DIR_LOADS); +#else + smp_store_release(&dir->cached_dir, cached); +#endif return 0; } From ed1cdaa5b0454bef5a99b41ee56959452f288975 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 16:16:34 -0400 Subject: [PATCH 08/20] actiondfs: match identical blob digest pointers before CAS roots --- kernel/actiondfs/actiondfs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index f75359f..6c3d94b 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -1889,10 +1889,10 @@ actiondfs_find_blob_path_cache(struct actiondfs_sb_info *sbi, hash_for_each_possible_rcu(actiondfs_blob_path_cache, entry, hnode, key, lockdep_is_held(&actiondfs_blob_path_cache_lock)) { - if (entry->cas_root == sbi->cas_path.dentry && - (entry->hash == hash || - (actiondfs_digest_cache_key(entry->hash) == key && - !memcmp(entry->hash, hash, ACTIONDFS_HASH_LEN)))) + if (entry->hash == hash || + (entry->cas_root == sbi->cas_path.dentry && + actiondfs_digest_cache_key(entry->hash) == key && + !memcmp(entry->hash, hash, ACTIONDFS_HASH_LEN))) return entry; } return NULL; From e7eac199222545ae39e7de55557cdf1d09e2f5e6 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 16:17:57 -0400 Subject: [PATCH 09/20] actiondfs: match identical directory digests before CAS roots --- kernel/actiondfs/actiondfs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index 6c3d94b..819e2ce 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -1996,10 +1996,10 @@ actiondfs_find_cached_dir(struct actiondfs_sb_info *sbi, hash_for_each_possible_rcu(actiondfs_dir_cache, entry, hnode, key, lockdep_is_held(&actiondfs_dir_cache_lock)) { - if (entry->cas_root == sbi->cas_path.dentry && - (entry->hash == hash || - (actiondfs_digest_cache_key(entry->hash) == key && - !memcmp(entry->hash, hash, ACTIONDFS_HASH_LEN)))) + if (entry->hash == hash || + (entry->cas_root == sbi->cas_path.dentry && + actiondfs_digest_cache_key(entry->hash) == key && + !memcmp(entry->hash, hash, ACTIONDFS_HASH_LEN))) return entry; } return NULL; From 207028253e62ff6723d8e32db3c5527768c10f85 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 16:24:37 -0400 Subject: [PATCH 10/20] actiondfs: compress cached child records with name offsets --- kernel/actiondfs/actiondfs.c | 85 +++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index 819e2ce..2a194a2 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -74,8 +74,8 @@ static const u8 actiondfs_empty_sha256[ACTIONDFS_HASH_LEN] = { }; struct actiondfs_cached_child { - char *name; u64 size; + u32 name_offset; u16 name_len; umode_t mode; union { @@ -84,6 +84,8 @@ struct actiondfs_cached_child { }; }; +static_assert(sizeof(struct actiondfs_cached_child) == 48); + struct actiondfs_cached_children { struct actiondfs_cached_child *entries; u32 count; @@ -422,6 +424,13 @@ static struct actiondfs_sb_info *actiondfs_sbi(struct super_block *sb) return sb->s_fs_info; } +static const char *actiondfs_cached_child_name( + const struct actiondfs_cached_dir *dir, + const struct actiondfs_cached_child *child) +{ + return dir->names + child->name_offset; +} + static void actiondfs_copy_stage_owner(struct inode *inode, struct inode *real_inode) { @@ -662,6 +671,8 @@ static int actiondfs_ensure_stage_parent_path(struct actiondfs_sb_info *sbi, struct actiondfs_node *ancestor = ancestors[--depth]; const struct actiondfs_cached_child *child = ancestor->input_child; + const char *name = actiondfs_cached_child_name( + ancestor->parent->cached_dir, child); struct path next_path; if (smp_load_acquire(&ancestor->stage_dentry)) { @@ -670,19 +681,19 @@ static int actiondfs_ensure_stage_parent_path(struct actiondfs_sb_info *sbi, } else { actiondfs_stat_inc(ACTIONDFS_STAT_STAGE_ENSURE_DIR_COMPONENTS); err = vfs_path_lookup(current_path.dentry, current_path.mnt, - child->name, + name, LOOKUP_DIRECTORY | LOOKUP_NO_SYMLINKS | LOOKUP_NO_XDEV, &next_path); if (err == -ENOENT) { err = actiondfs_stage_mkdir_child( - ¤t_path, child->name, child->name_len, + ¤t_path, name, child->name_len, ancestor->mode & S_IALLUGO); if (!err) { actiondfs_stat_inc( ACTIONDFS_STAT_STAGE_ENSURE_DIR_CREATED); err = vfs_path_lookup( current_path.dentry, current_path.mnt, - child->name, + name, LOOKUP_DIRECTORY | LOOKUP_NO_SYMLINKS | LOOKUP_NO_XDEV, &next_path); } @@ -728,17 +739,19 @@ static int actiondfs_compare_name(const char *lhs, size_t lhs_len, } static struct actiondfs_cached_child * -actiondfs_find_cached_child_in(struct actiondfs_cached_children *children, +actiondfs_find_cached_child_in(struct actiondfs_cached_dir *dir, const char *name, size_t len) { + struct actiondfs_cached_children *children = &dir->children; size_t lo = 0; size_t hi = children->count; while (lo < hi) { size_t mid = lo + (hi - lo) / 2; struct actiondfs_cached_child *child = &children->entries[mid]; - int cmp = actiondfs_compare_name(child->name, child->name_len, - name, len); + int cmp = actiondfs_compare_name( + actiondfs_cached_child_name(dir, child), child->name_len, + name, len); if (cmp < 0) { lo = mid + 1; @@ -759,7 +772,7 @@ actiondfs_find_cached_child(struct actiondfs_node *dir, if (!cached) return NULL; - return actiondfs_find_cached_child_in(&cached->children, name, len); + return actiondfs_find_cached_child_in(cached, name, len); } static struct actiondfs_node * @@ -777,27 +790,32 @@ actiondfs_materialize_cached_child(struct actiondfs_node *parent, node->ino = actiondfs_input_child_ino(parent, record); node->size = record->size; if (S_ISLNK(record->mode)) - node->link_target = record->name + record->name_len + 1; + node->link_target = (char *)record->target; return node; } -static int actiondfs_compare_cached_children(const void *lhs, const void *rhs) +static int actiondfs_compare_cached_children(const void *lhs, const void *rhs, + const void *data) { const struct actiondfs_cached_child *left = lhs; const struct actiondfs_cached_child *right = rhs; + const char *names = data; - return actiondfs_compare_name(left->name, left->name_len, - right->name, right->name_len); + return actiondfs_compare_name(names + left->name_offset, + left->name_len, + names + right->name_offset, + right->name_len); } -static int actiondfs_sort_cached_children(struct actiondfs_cached_dir *dir) +static int actiondfs_sort_cached_children(struct actiondfs_cached_dir *dir, + const u8 *source) { struct actiondfs_cached_children *children = &dir->children; size_t i; for (i = 1; i < children->count; i++) { int cmp = actiondfs_compare_cached_children( - &children->entries[i - 1], &children->entries[i]); + &children->entries[i - 1], &children->entries[i], source); if (!cmp) return -EEXIST; @@ -807,12 +825,13 @@ static int actiondfs_sort_cached_children(struct actiondfs_cached_dir *dir) return 0; sort: - sort_nonatomic(children->entries, children->count, - sizeof(*children->entries), - actiondfs_compare_cached_children, NULL); + sort_r_nonatomic(children->entries, children->count, + sizeof(*children->entries), + actiondfs_compare_cached_children, NULL, source); for (i = 1; i < children->count; i++) { if (!actiondfs_compare_cached_children( - &children->entries[i - 1], &children->entries[i])) + &children->entries[i - 1], &children->entries[i], + source)) return -EEXIST; } return 0; @@ -890,6 +909,7 @@ static void actiondfs_destroy_blob_path_cache(void) } static int actiondfs_append_cached_child(struct actiondfs_cached_children *children, + const u8 *source, const char *name, size_t name_len, umode_t mode, @@ -910,8 +930,9 @@ static int actiondfs_append_cached_child(struct actiondfs_cached_children *child return err; if (previous != U32_MAX) { child = &children->entries[previous]; - if (actiondfs_compare_name(child->name, child->name_len, - name, name_len) >= 0) + if (actiondfs_compare_name((const char *)source + + child->name_offset, + child->name_len, name, name_len) >= 0) return -EINVAL; } @@ -933,7 +954,7 @@ static int actiondfs_append_cached_child(struct actiondfs_cached_children *child } child = &children->entries[children->count]; - child->name = (char *)name; + child->name_offset = (const u8 *)name - source; child->name_len = name_len; child->mode = mode; child->size = size; @@ -945,7 +966,8 @@ static int actiondfs_append_cached_child(struct actiondfs_cached_children *child return 0; } -static int actiondfs_pack_cached_dir_names(struct actiondfs_cached_dir *dir) +static int actiondfs_pack_cached_dir_names(struct actiondfs_cached_dir *dir, + const u8 *source) { struct actiondfs_cached_children *children = &dir->children; size_t total = 0; @@ -980,8 +1002,8 @@ static int actiondfs_pack_cached_dir_names(struct actiondfs_cached_dir *dir) struct actiondfs_cached_child *child = &children->entries[i]; const char *target = S_ISLNK(child->mode) ? child->target : NULL; - memcpy(next, child->name, child->name_len); - child->name = next; + memcpy(next, source + child->name_offset, child->name_len); + child->name_offset = next - dir->names; next += child->name_len; *next++ = '\0'; if (target) { @@ -1755,6 +1777,7 @@ static int actiondfs_parse_reapi_child_fields(const u8 *data, size_t len, } static int actiondfs_parse_reapi_cached_child(struct actiondfs_cached_dir *parent, + const u8 *source, const u8 *data, size_t len, umode_t mode, u32 *previous) { @@ -1767,7 +1790,7 @@ static int actiondfs_parse_reapi_cached_child(struct actiondfs_cached_dir *paren return err; if (S_ISLNK(mode)) { err = actiondfs_append_cached_child( - &parent->children, child.name, child.name_len, + &parent->children, source, child.name, child.name_len, S_IFLNK | 0777, child.symlink.target_len, NULL, child.symlink.target, *previous); if (!err) @@ -1780,7 +1803,7 @@ static int actiondfs_parse_reapi_cached_child(struct actiondfs_cached_dir *paren mode |= child.executable ? 0555 : 0444; else mode |= ACTIONDFS_DIR_MODE; - err = actiondfs_append_cached_child(&parent->children, + err = actiondfs_append_cached_child(&parent->children, source, child.name, child.name_len, mode, child.digest.size, child.digest.hash, NULL, *previous); @@ -2063,7 +2086,7 @@ static int actiondfs_build_cached_dir(struct actiondfs_sb_info *sbi, if (err) goto out_buffer; err = actiondfs_parse_reapi_cached_child( - entry, field, field_len, + entry, buffer, field, field_len, (key >> 3) == 1 ? S_IFREG : (key >> 3) == 2 ? S_IFDIR : S_IFLNK, &previous[(key >> 3) - 1]); @@ -2077,10 +2100,10 @@ static int actiondfs_build_cached_dir(struct actiondfs_sb_info *sbi, } } - err = actiondfs_sort_cached_children(entry); + err = actiondfs_sort_cached_children(entry, buffer); if (err) goto out_buffer; - err = actiondfs_pack_cached_dir_names(entry); + err = actiondfs_pack_cached_dir_names(entry, buffer); if (err) goto out_buffer; @@ -3170,8 +3193,10 @@ static bool actiondfs_emit_cached_children( for (; index < children->count; index++) { struct actiondfs_cached_child *child = &children->entries[index]; + const char *name = actiondfs_cached_child_name(dir->cached_dir, + child); - if (!dir_emit(ctx, child->name, child->name_len, + if (!dir_emit(ctx, name, child->name_len, actiondfs_input_child_ino(dir, child), fs_umode_to_dtype(child->mode))) return false; From 7de37eb8fa3a98795abf6aa219daed9d4b8035cf Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 16:45:13 -0400 Subject: [PATCH 11/20] actiondfs: allocate directory iteration state only for staged entries --- kernel/actiondfs/actiondfs.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index 2a194a2..74256df 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -3215,20 +3215,8 @@ static int actiondfs_iterate_shared(struct file *file, struct dir_context *ctx) loff_t base = 2; int err; - if (!dir_file) { - struct actiondfs_dir_file *existing; - - dir_file = kzalloc(sizeof(*dir_file), GFP_KERNEL); - if (!dir_file) - return -ENOMEM; - existing = cmpxchg(&file->private_data, NULL, dir_file); - if (existing) { - kfree(dir_file); - dir_file = existing; - } - } - - if (!ctx->pos && (dir_file->stage_file || dir_file->stage_eof)) + if (dir_file && !ctx->pos && + (dir_file->stage_file || dir_file->stage_eof)) actiondfs_reset_stage_file(dir_file); err = actiondfs_ensure_loaded(inode->i_sb, dir); @@ -3250,6 +3238,21 @@ static int actiondfs_iterate_shared(struct file *file, struct dir_context *ctx) return 0; } + if (!READ_ONCE(dir->stage_dentry)) + return 0; + if (!dir_file) { + struct actiondfs_dir_file *existing; + + dir_file = kzalloc(sizeof(*dir_file), GFP_KERNEL); + if (!dir_file) + return -ENOMEM; + existing = cmpxchg(&file->private_data, NULL, dir_file); + if (existing) { + kfree(dir_file); + dir_file = existing; + } + } + return actiondfs_emit_stage_entries(inode, dir, dir_file, ctx, base); } From 5948d228d9f813c1c33a033e320444b4023540f9 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 16:45:48 -0400 Subject: [PATCH 12/20] actiondfs: skip sorting directories with one validated child type --- kernel/actiondfs/actiondfs.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index 74256df..73ab80e 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -2100,9 +2100,13 @@ static int actiondfs_build_cached_dir(struct actiondfs_sb_info *sbi, } } - err = actiondfs_sort_cached_children(entry, buffer); - if (err) - goto out_buffer; + if ((previous[0] != U32_MAX) + + (previous[1] != U32_MAX) + + (previous[2] != U32_MAX) > 1) { + err = actiondfs_sort_cached_children(entry, buffer); + if (err) + goto out_buffer; + } err = actiondfs_pack_cached_dir_names(entry, buffer); if (err) goto out_buffer; From 9233204a0aff3bad91cc1e7fb01ed0cc39b60a6b Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 16:47:00 -0400 Subject: [PATCH 13/20] actiondfs: decode single-byte protobuf varints directly --- kernel/actiondfs/actiondfs.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index 73ab80e..88f814b 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -1573,6 +1573,11 @@ static int actiondfs_pb_read_varint(const u8 *data, size_t len, u64 out = 0; unsigned int shift = 0; + if (*pos < len && !(data[*pos] & 0x80)) { + *value = data[(*pos)++]; + return 0; + } + while (*pos < len && shift < 64) { u8 byte = data[(*pos)++]; From 28a49b0c76fc439a6e2dedfdf3d1b5a158fafbc7 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 16:48:42 -0400 Subject: [PATCH 14/20] actiondfs: avoid owner spinlocks when staged identities already match --- kernel/actiondfs/actiondfs.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index 88f814b..00fcf8a 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -2478,9 +2478,15 @@ static struct inode *actiondfs_lookup_staged_inode(struct inode *dir, } if (inode->i_private != node) { node = inode->i_private; - spin_lock(&inode->i_lock); - actiondfs_copy_stage_owner(inode, real_inode); - spin_unlock(&inode->i_lock); + if (mnt_idmap(sbi->stage_path.mnt) != &nop_mnt_idmap || + !uid_eq(READ_ONCE(inode->i_uid), + READ_ONCE(real_inode->i_uid)) || + !gid_eq(READ_ONCE(inode->i_gid), + READ_ONCE(real_inode->i_gid))) { + spin_lock(&inode->i_lock); + actiondfs_copy_stage_owner(inode, real_inode); + spin_unlock(&inode->i_lock); + } actiondfs_set_stage_dentry(node, real_dentry); } if (S_ISDIR(mode)) { From e7bd186e85a8d4c9bdd19d77c0926e5ad1fd307d Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 16:49:13 -0400 Subject: [PATCH 15/20] actiondfs: assign immutable inode numbers only once --- kernel/actiondfs/actiondfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index 00fcf8a..d0322a2 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -2261,7 +2261,8 @@ static int actiondfs_set_input_inode(struct inode *inode, void *data) static void actiondfs_init_inode(struct inode *inode, struct actiondfs_node *node) { - inode->i_ino = node->ino; + if (!node->input_child) + inode->i_ino = node->ino; if (node->stage_dentry) { inode->i_mode = node->mode; actiondfs_copy_stage_owner(inode, d_inode(node->stage_dentry)); From 24ec5a130899550b7c0c18ae1d80392ee6f1e70b Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 16:50:24 -0400 Subject: [PATCH 16/20] actiondfs: initialize only required protobuf child fields --- kernel/actiondfs/actiondfs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index d0322a2..0f349d2 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -1721,7 +1721,12 @@ static int actiondfs_parse_reapi_child_fields(const u8 *data, size_t len, size_t pos = 0; int err; - memset(out, 0, sizeof(*out)); + out->name = NULL; + out->executable = false; + if (S_ISLNK(mode)) + out->symlink.target = NULL; + else + out->digest.present = false; while (pos < len) { const u8 *field; size_t field_len; From 34838bce2c9f68c79f0f420f80831c2c9e589699 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 16:57:26 -0400 Subject: [PATCH 17/20] actiondfs: accumulate packed filename bytes while parsing children --- kernel/actiondfs/actiondfs.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index 0f349d2..ea6355c 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -910,6 +910,7 @@ static void actiondfs_destroy_blob_path_cache(void) static int actiondfs_append_cached_child(struct actiondfs_cached_children *children, const u8 *source, + size_t *names_bytes, const char *name, size_t name_len, umode_t mode, @@ -920,6 +921,8 @@ static int actiondfs_append_cached_child(struct actiondfs_cached_children *child { struct actiondfs_cached_child *entries; struct actiondfs_cached_child *child; + size_t packed_bytes; + size_t total; u32 capacity; int err; @@ -936,6 +939,13 @@ static int actiondfs_append_cached_child(struct actiondfs_cached_children *child return -EINVAL; } + packed_bytes = name_len + 1; + if (target && + check_add_overflow(packed_bytes, (size_t)size + 1, &packed_bytes)) + return -EOVERFLOW; + if (check_add_overflow(*names_bytes, packed_bytes, &total)) + return -EOVERFLOW; + if (children->count == children->capacity) { size_t bytes; @@ -963,28 +973,19 @@ static int actiondfs_append_cached_child(struct actiondfs_cached_children *child else memcpy(child->hash, hash, ACTIONDFS_HASH_LEN); children->count++; + *names_bytes = total; return 0; } static int actiondfs_pack_cached_dir_names(struct actiondfs_cached_dir *dir, - const u8 *source) + const u8 *source, + size_t total) { struct actiondfs_cached_children *children = &dir->children; - size_t total = 0; size_t available; char *next; size_t i; - for (i = 0; i < children->count; i++) { - struct actiondfs_cached_child *child = &children->entries[i]; - - if (check_add_overflow(total, (size_t)child->name_len + 1, - &total)) - return -EOVERFLOW; - if (S_ISLNK(child->mode) && - check_add_overflow(total, (size_t)child->size + 1, &total)) - return -EOVERFLOW; - } if (!total) return 0; @@ -1788,6 +1789,7 @@ static int actiondfs_parse_reapi_child_fields(const u8 *data, size_t len, static int actiondfs_parse_reapi_cached_child(struct actiondfs_cached_dir *parent, const u8 *source, + size_t *names_bytes, const u8 *data, size_t len, umode_t mode, u32 *previous) { @@ -1800,7 +1802,8 @@ static int actiondfs_parse_reapi_cached_child(struct actiondfs_cached_dir *paren return err; if (S_ISLNK(mode)) { err = actiondfs_append_cached_child( - &parent->children, source, child.name, child.name_len, + &parent->children, source, names_bytes, child.name, + child.name_len, S_IFLNK | 0777, child.symlink.target_len, NULL, child.symlink.target, *previous); if (!err) @@ -1814,6 +1817,7 @@ static int actiondfs_parse_reapi_cached_child(struct actiondfs_cached_dir *paren else mode |= ACTIONDFS_DIR_MODE; err = actiondfs_append_cached_child(&parent->children, source, + names_bytes, child.name, child.name_len, mode, child.digest.size, child.digest.hash, NULL, *previous); @@ -2047,6 +2051,7 @@ static int actiondfs_build_cached_dir(struct actiondfs_sb_info *sbi, u8 *buffer; size_t len; size_t pos = 0; + size_t names_bytes = 0; size_t hash_bytes; u32 previous[3] = { U32_MAX, U32_MAX, U32_MAX }; int err; @@ -2096,7 +2101,7 @@ static int actiondfs_build_cached_dir(struct actiondfs_sb_info *sbi, if (err) goto out_buffer; err = actiondfs_parse_reapi_cached_child( - entry, buffer, field, field_len, + entry, buffer, &names_bytes, field, field_len, (key >> 3) == 1 ? S_IFREG : (key >> 3) == 2 ? S_IFDIR : S_IFLNK, &previous[(key >> 3) - 1]); @@ -2117,7 +2122,7 @@ static int actiondfs_build_cached_dir(struct actiondfs_sb_info *sbi, if (err) goto out_buffer; } - err = actiondfs_pack_cached_dir_names(entry, buffer); + err = actiondfs_pack_cached_dir_names(entry, buffer, names_bytes); if (err) goto out_buffer; From 7d9d5c64acba8dc72a188f6079235bce8d0e9cf1 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 17:02:47 -0400 Subject: [PATCH 18/20] actiondfs: borrow guaranteed backing files on nonempty input reads --- kernel/actiondfs/actiondfs.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index ea6355c..532226d 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -1302,10 +1302,8 @@ static ssize_t actiondfs_read_iter(struct kiocb *iocb, struct iov_iter *to) iov_iter_truncate(to, node->size - iocb->ki_pos); - file = actiondfs_get_node_blob_file(iocb->ki_filp); - if (IS_ERR(file)) - return PTR_ERR(file); - + file = iocb->ki_filp->private_data; + actiondfs_stat_inc(ACTIONDFS_STAT_NODE_BLOB_CACHE_HITS); actiondfs_stat_inc(ACTIONDFS_STAT_BACKING_READS); nread = actiondfs_backing_read_iter(file, to, iocb); @@ -1433,14 +1431,11 @@ static ssize_t actiondfs_copy_file_range(struct file *file_in, loff_t pos_in, goto out; len = min_t(u64, (u64)len, node_in->size - pos_in); if (node_in->origin == ACTIONDFS_NODE_INPUT) { - real_in = actiondfs_get_node_blob_file(file_in); + real_in = file_in->private_data; + actiondfs_stat_inc(ACTIONDFS_STAT_NODE_BLOB_CACHE_HITS); } else { real_in = file_in->private_data; } - if (IS_ERR(real_in)) { - copied = PTR_ERR(real_in); - goto out; - } real_out = file_out->private_data; inode_lock(inode_out); @@ -1506,10 +1501,8 @@ static ssize_t actiondfs_splice_read(struct file *actiondfs_file, loff_t *ppos, wanted = min_t(u64, (u64)len, node->size - pos); - file = actiondfs_get_node_blob_file(actiondfs_file); - if (IS_ERR(file)) - return PTR_ERR(file); - + file = actiondfs_file->private_data; + actiondfs_stat_inc(ACTIONDFS_STAT_NODE_BLOB_CACHE_HITS); actiondfs_stat_inc(ACTIONDFS_STAT_SPLICE_READS); nread = vfs_splice_read(file, &pos, pipe, wanted, flags); if (nread > 0) { From 964e3707a5d4e3f5d62293cee4e6a32f160c860c Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 17:07:35 -0400 Subject: [PATCH 19/20] actiondfs: decode directory digests directly into cached children --- kernel/actiondfs/actiondfs.c | 66 ++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index 532226d..0875b2d 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -908,6 +908,29 @@ static void actiondfs_destroy_blob_path_cache(void) mutex_unlock(&actiondfs_blob_path_cache_lock); } +static int actiondfs_reserve_cached_child( + struct actiondfs_cached_children *children) +{ + struct actiondfs_cached_child *entries; + size_t bytes; + u32 capacity; + + if (children->count < children->capacity) + return 0; + if (children->capacity > U32_MAX / 2) + return -EOVERFLOW; + capacity = children->capacity ? children->capacity * 2 : 4; + bytes = kmalloc_size_roundup(array_size(capacity, sizeof(*entries))); + capacity = min_t(size_t, bytes / sizeof(*entries), U32_MAX); + entries = krealloc_array(children->entries, capacity, + sizeof(*entries), GFP_KERNEL); + if (!entries) + return -ENOMEM; + children->entries = entries; + children->capacity = capacity; + return 0; +} + static int actiondfs_append_cached_child(struct actiondfs_cached_children *children, const u8 *source, size_t *names_bytes, @@ -915,15 +938,12 @@ static int actiondfs_append_cached_child(struct actiondfs_cached_children *child size_t name_len, umode_t mode, u64 size, - const u8 *hash, const char *target, u32 previous) { - struct actiondfs_cached_child *entries; struct actiondfs_cached_child *child; size_t packed_bytes; size_t total; - u32 capacity; int err; if (target && (!size || size >= PATH_MAX || memchr(target, '\0', size))) @@ -946,23 +966,6 @@ static int actiondfs_append_cached_child(struct actiondfs_cached_children *child if (check_add_overflow(*names_bytes, packed_bytes, &total)) return -EOVERFLOW; - if (children->count == children->capacity) { - size_t bytes; - - if (children->capacity > U32_MAX / 2) - return -EOVERFLOW; - capacity = children->capacity ? children->capacity * 2 : 4; - bytes = kmalloc_size_roundup(array_size(capacity, - sizeof(*entries))); - capacity = min_t(size_t, bytes / sizeof(*entries), U32_MAX); - entries = krealloc_array(children->entries, capacity, - sizeof(*entries), GFP_KERNEL); - if (!entries) - return -ENOMEM; - children->entries = entries; - children->capacity = capacity; - } - child = &children->entries[children->count]; child->name_offset = (const u8 *)name - source; child->name_len = name_len; @@ -970,8 +973,6 @@ static int actiondfs_append_cached_child(struct actiondfs_cached_children *child child->size = size; if (target) child->target = target; - else - memcpy(child->hash, hash, ACTIONDFS_HASH_LEN); children->count++; *names_bytes = total; return 0; @@ -1641,7 +1642,6 @@ static int actiondfs_pb_skip(const u8 *data, size_t len, size_t *pos, u64 wire) } struct actiondfs_reapi_digest { - u8 hash[ACTIONDFS_HASH_LEN]; u64 size; bool present; }; @@ -1660,7 +1660,8 @@ struct actiondfs_parsed_child { }; static int actiondfs_parse_reapi_digest(const u8 *data, size_t len, - struct actiondfs_reapi_digest *digest) + struct actiondfs_reapi_digest *digest, + u8 *hash) { size_t pos = 0; int err; @@ -1684,7 +1685,7 @@ static int actiondfs_parse_reapi_digest(const u8 *data, size_t len, if (err) return err; if (field_len != ACTIONDFS_HASH_HEX_LEN || - hex2bin(digest->hash, (const char *)field, + hex2bin(hash, (const char *)field, ACTIONDFS_HASH_LEN)) return -EINVAL; digest->present = true; @@ -1710,7 +1711,7 @@ static int actiondfs_parse_reapi_digest(const u8 *data, size_t len, static int actiondfs_parse_reapi_child_fields(const u8 *data, size_t len, struct actiondfs_parsed_child *out, - umode_t mode) + umode_t mode, u8 *hash) { size_t pos = 0; int err; @@ -1747,7 +1748,7 @@ static int actiondfs_parse_reapi_child_fields(const u8 *data, size_t len, out->symlink.target_len = field_len; } else { err = actiondfs_parse_reapi_digest(field, field_len, - &out->digest); + &out->digest, hash); if (err) return err; } @@ -1790,7 +1791,12 @@ static int actiondfs_parse_reapi_cached_child(struct actiondfs_cached_dir *paren bool regular; int err; - err = actiondfs_parse_reapi_child_fields(data, len, &child, mode); + err = actiondfs_reserve_cached_child(&parent->children); + if (err) + return err; + err = actiondfs_parse_reapi_child_fields( + data, len, &child, mode, + parent->children.entries[parent->children.count].hash); if (err) return err; if (S_ISLNK(mode)) { @@ -1798,7 +1804,7 @@ static int actiondfs_parse_reapi_cached_child(struct actiondfs_cached_dir *paren &parent->children, source, names_bytes, child.name, child.name_len, S_IFLNK | 0777, child.symlink.target_len, - NULL, child.symlink.target, *previous); + child.symlink.target, *previous); if (!err) *previous = parent->children.count - 1; return err; @@ -1813,7 +1819,7 @@ static int actiondfs_parse_reapi_cached_child(struct actiondfs_cached_dir *paren names_bytes, child.name, child.name_len, mode, child.digest.size, - child.digest.hash, NULL, *previous); + NULL, *previous); if (!err) { *previous = parent->children.count - 1; if (!regular) From d1bd7bb9c8dde8e7c2cf319d6c7fb5e352055f77 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sun, 2 Aug 2026 17:10:22 -0400 Subject: [PATCH 20/20] actiondfs: publish immutable inode identity only in the hash initializer --- e2e/LLVM_VM_SMOKE_TIMINGS.md | 251 +++++++++++++++++++---------------- kernel/actiondfs/actiondfs.c | 5 +- test/STRESS_TIMINGS.md | 240 ++++++++++++++++----------------- 3 files changed, 256 insertions(+), 240 deletions(-) diff --git a/e2e/LLVM_VM_SMOKE_TIMINGS.md b/e2e/LLVM_VM_SMOKE_TIMINGS.md index 3d370e8..2d5d49c 100644 --- a/e2e/LLVM_VM_SMOKE_TIMINGS.md +++ b/e2e/LLVM_VM_SMOKE_TIMINGS.md @@ -27,23 +27,24 @@ subset of the VM `llvm-tblgen` graph. ## Latest Checked-In Result -- Generated: `2026-08-01 23:15:01 EDT` +- Generated: `2026-08-02 17:30:51 EDT` - Command: `ACTIOND_BAZEL_BUILD_FLAGS="--jobs=32 --distdir=/private/tmp/actiond-audit-distdir --override_repository=llvm++osx+macos_sdk=/private/tmp/actiond-audit-macos-sdk" ACTIOND_LLVM_SMOKE_MAC_HOST=0 e2e/run_llvm_vm_smoke.sh` -- Output root: `/var/folders/p4/xn8y5q_j24l5xwgwd_jx5c340000gn/T/actiond-llvm-vm-smoke.J4l9IY` +- Output root: `/var/folders/p4/xn8y5q_j24l5xwgwd_jx5c340000gn/T/actiond-llvm-vm-smoke.gybdrT` - Workload: `@llvm-project//llvm:llvm-tblgen`, jobs=8 - VM warmup target: `//e2e:llvm_exec_warmup` - Target and VM host platform: `@llvm//platforms:linux_arm64_musl` - Build mode: `-c opt --strip=always --stripopt=--strip-all` -- VM warmup: `92.411s`; `2207 processes: 190 internal, 2017 remote` -- Measured VM build: `109.140s`; `2310 processes: 4 action cache hit, 204 internal, 2106 remote` +- VM warmup: `96.797s`; `2207 processes: 190 internal, 2017 remote` +- Measured VM build: `117.050s`; `2310 processes: 4 action cache hit, 204 internal, 2106 remote` - Measured executions and timing records: `2106` - Mac-host baseline: not run (`ACTIOND_LLVM_SMOKE_MAC_HOST=0`) -The prior checked-in run completed the warmup in `97.375s` and the measured -build in `144.161s` with the same 2,017 warmup actions and 2,106 measured -actions; the current measured build is 24.3% faster. An intermediate run after -the first ten changes completed the warmup in `151.588s` and the measured -build in `155.934s`. Earlier runs of the same action sets completed in +The prior checked-in run completed the warmup in `92.411s` and the measured +build in `109.140s` with the same 2,017 warmup actions and 2,106 measured +actions. The current warmup is 4.7% slower and the measured build is 7.2% +slower. An earlier checked-in run completed in `97.375s`/`144.161s`, and an +intermediate run after the first ten changes completed in +`151.588s`/`155.934s`. Earlier runs of the same action sets completed in `90.262s`/`98.569s`, `82.129s`/`92.336s`, `63.992s`/`86.002s`, `96.032s`/`104.393s`, and `94.614s`/`180.238s`. Host CPU and memory contention produce substantial variation, so these local elapsed times do not isolate an @@ -62,67 +63,81 @@ Counters include both warmup and measured builds. | Counter | Prior | Current | | ------------------------------------ | ----------: | ----------: | -| actiondfs mounts | 4,123 | 4,123 | -| root directory parses | 1,407 | 1,406 | -| directory loads | 169,665 | 169,665 | -| cached directory requests | 169,665 | 169,665 | -| cached directory hits | 163,042 | 163,046 | -| cached directory misses | 6,623 | 6,619 | -| cached directory races | 4 | 0 | -| cached file records | 16,723 | 16,722 | -| cached directory records | 12,937 | 12,934 | -| directory blob reads | 6,622 | 6,618 | -| staged ensure-directory calls | 16,000 | 15,999 | +| actiondfs mounts | 4,123 | 4,122 | +| root directory parses | 1,406 | 1,406 | +| directory loads | 169,665 | 169,617 | +| cached directory requests | 169,665 | 169,617 | +| cached directory hits | 163,046 | 163,009 | +| cached directory misses | 6,619 | 6,608 | +| cached directory races | 0 | 2 | +| cached file records | 16,722 | 16,634 | +| cached directory records | 12,934 | 12,914 | +| directory blob reads | 6,618 | 6,607 | +| staged ensure-directory calls | 15,999 | 15,996 | | staged ensure-directory components | 16 | 16 | | staged ensure-directory existing | 0 | 0 | | staged ensure-directory created | 16 | 16 | -| staged parent dentry hits | 15,984 | 15,983 | -| blob-path cache hits | 460,968 | 460,970 | -| blob-path cache misses | 6,972 | 6,970 | +| staged parent dentry hits | 15,983 | 15,980 | +| blob-path cache hits | 460,970 | 460,692 | +| blob-path cache misses | 6,970 | 6,883 | | blob-path cache evictions | 0 | 0 | -| blob-path cache insertion races | 2 | 0 | -| backing reads | 415,500 | 415,500 | -| input backing-file acquisitions | 472,567 | 472,567 | -| mmap calls | 53,239 | 53,239 | -| staged write calls | 37,194 | 37,194 | -| staged backing open attempts | 15,828 | 15,827 | -| staged backing open lookup | 2.606 ms | 0.296 ms | +| blob-path cache insertion races | 0 | 0 | +| backing reads | 415,500 | 415,332 | +| input backing-file acquisitions | 472,567 | 472,190 | +| mmap calls | 53,239 | 53,030 | +| staged write calls | 37,194 | 37,072 | +| staged backing open attempts | 15,827 | 15,825 | +| staged backing open lookup | 0.296 ms | 0.295 ms | | staged `copy_file_range` successes | 3,828 | 3,828 | | staged `copy_file_range` fallbacks | 0 | 0 | The workload retained the same 2,017 warmup actions and 2,106 measured actions. -The 163,046 cached-directory hits used RCU without a per-node mutex. The -1,407,098 lookups retained the staged-negative filter, while cached input inode +The 163,009 cached-directory hits used RCU without a per-node mutex. The +1,406,668 lookups retained the staged-negative filter, while cached input inode lookups used `iget5_locked_rcu` without the global inode-hash lookup lock; hashed inode identity and filesystem watches were preserved. Cached-directory checks were inlined without constructing the directory-parser stack frame, and input inode numbers used cached-child indexes instead of hashing filenames. Immutable input inodes reused their mount root's timestamps. -The 16,722 cached file records and 12,934 cached directory records stored -32-byte binary SHA-256 digests instead of 64-byte hexadecimal digests, saving -948,992 bytes across those 29,656 records. Each cached directory used one +The 16,634 cached file records and 12,914 cached directory records used 48-byte +cached-child records. Replacing each filename pointer with a 32-bit name offset +saved 236,384 bytes across those 29,548 records. Their 32-byte binary SHA-256 +digests saved another 945,536 bytes compared with hexadecimal digests, and +decoding directly into each cached child avoided another 945,536 bytes of +temporary digest copies. Each cached directory used one sorted child vector instead of separate file, directory, and symlink vectors; packed filenames reused unused child-vector capacity when possible and needed a separate allocation only when that capacity was insufficient. Cached-directory records borrowed persistent child digests, compared matching digest pointers before bytes, rejected full cache-key mismatches before digest comparisons, and skipped sorting already ordered children. +Directories containing one child type skipped sorting entirely, protobuf +parsing decoded single-byte varints directly, and packed filename sizes were +accumulated while parsing instead of rescanning completed child records. Each filesystem node omitted its former directory-loading mutex, and each of -the 4,123 mounts borrowed its mount-option buffer. - -The 472,567 input backing-file acquisitions borrowed open backing files without -per-operation references. The 415,500 synchronous backing reads, 53,239 mmap -calls, and 37,194 synchronous staged writes avoided redundant credential swaps. +the 4,122 mounts borrowed its mount-option buffer. Production cached-directory +publication used a release store instead of an atomic exchange for each +directory load; this instrumented run recorded 169,617 directory loads and +retained atomic exchanges for exact race statistics. + +The 472,190 input backing-file acquisitions borrowed open backing files without +per-operation references. The 415,332 synchronous backing reads, 53,030 mmap +calls, and 37,072 synchronous staged writes avoided redundant credential swaps. Staged creates reused VFS negative lookup results, initialized staged ownership directly, and retained backing-inode ownership through the staged mount idmap. Directory-blob closes and blob-cache entry releases omitted deferred close and -release workqueues. Staged create, mkdir, unlink, rename, size updates, mmap, -and child lookups reported zero failures. +release workqueues; private directory-blob handles also avoided global file +accounting. Staged create, mkdir, unlink, rename, size updates, mmap, and child +lookups reported zero failures. Staged-directory iteration allocated state +only when staged entries existed; the separate synthetic VM stress run +recorded 13 staged-directory hits with zero misses. Input inode owners used `current_fsuid_fsgid`, while identity-mapped staged -inodes copied backing UID/GID directly instead of performing idmap -conversions. Synchronous backing reads and staged writes constructed backing +inodes copied backing UID/GID directly instead of performing idmap conversions +or taking owner spinlocks when those identities already matched. Immutable +inode identity was initialized exactly once before RCU publication. Synchronous +backing reads and staged writes constructed backing credential contexts only for asynchronous or direct-I/O requests, and immutable input reads let `iov_iter_truncate` clamp the requested byte count directly. Directory attributes used `generic_fillattr` directly, while regular files and @@ -130,11 +145,11 @@ symlinks used the VFS generic attribute fallback. Staged writes and `copy_file_range` skipped `file_remove_privs` only when `IS_NOSEC` confirmed no security attributes required processing. -All 18,388 staged input-directory merges searched cached input directories, and -75,376 staged positive or negative inode lookups avoided exclusive backing -directory locks. Resolving 6,970 blob-path cache misses and 6,618 directory -blobs directly avoided 13,588 temporary `PATH_MAX` pathname allocations, or -about 53.1 MiB of cumulative allocation. Unsupported extended attributes were +All 18,382 staged input-directory merges searched cached input directories, and +75,360 staged positive or negative inode lookups avoided exclusive backing +directory locks. Resolving 6,883 blob-path cache misses and 6,607 directory +blobs directly avoided 13,490 temporary `PATH_MAX` pathname allocations, or +about 52.7 MiB of cumulative allocation. Unsupported extended attributes were skipped without bypassing inode permission checks, and backing filesystem security flags were inherited only when the backing filesystem advertised the same guarantees. @@ -145,10 +160,10 @@ All timing values are milliseconds. | Stage | Min | p25 | p50 | p75 | p95 | Mean | Max | | --------------------- | ----: | -----: | -----: | -----: | -------: | ------: | --------: | -| total | 4.379 | 23.305 | 31.561 | 84.933 | 1998.589 | 358.241 | 11136.456 | -| input fetch/setup | 0.423 | 0.589 | 0.643 | 1.031 | 2.596 | 1.749 | 480.404 | -| execute | 3.626 | 21.362 | 29.330 | 80.134 | 1993.589 | 354.955 | 11131.297 | -| output upload/collect | 0.022 | 0.138 | 0.280 | 1.604 | 5.317 | 1.537 | 106.624 | +| total | 6.250 | 21.243 | 31.585 | 96.870 | 2035.553 | 387.824 | 26588.199 | +| input fetch/setup | 0.408 | 0.578 | 0.619 | 0.974 | 2.850 | 1.676 | 342.861 | +| execute | 5.306 | 19.571 | 29.290 | 93.815 | 2028.774 | 384.406 | 26542.337 | +| output upload/collect | 0.023 | 0.127 | 0.224 | 1.308 | 5.351 | 1.742 | 192.466 | ## Runner Timing @@ -158,12 +173,12 @@ and lazy actiondfs input reads. All timing values are milliseconds. | Runner Stage | Min | p25 | p50 | p75 | p95 | Mean | Max | | -------------- | ----: | -----: | -----: | -----: | -------: | ------: | --------: | -| parent prepare | 0.061 | 0.101 | 0.121 | 0.156 | 0.452 | 0.225 | 33.856 | -| fork | 0.283 | 0.515 | 0.589 | 0.977 | 6.401 | 2.939 | 217.086 | -| child setup | 0.008 | 2.190 | 3.563 | 6.238 | 22.170 | 7.461 | 322.330 | -| process/io | 1.689 | 15.803 | 21.989 | 54.066 | 1987.070 | 343.441 | 11127.559 | -| wait | 0.000 | 0.000 | 0.000 | 0.000 | 4.653 | 0.681 | 32.240 | -| stdio digest | 0.000 | 0.000 | 0.001 | 0.001 | 0.001 | 0.001 | 0.106 | +| parent prepare | 0.055 | 0.100 | 0.125 | 0.158 | 0.464 | 0.343 | 214.156 | +| fork | 0.336 | 0.550 | 0.628 | 0.983 | 6.875 | 3.591 | 612.222 | +| child setup | 0.006 | 1.887 | 3.547 | 7.841 | 54.526 | 12.610 | 823.940 | +| process/io | 1.964 | 15.055 | 22.290 | 65.937 | 1999.065 | 367.155 | 26393.116 | +| wait | 0.000 | 0.000 | 0.000 | 0.000 | 3.027 | 0.325 | 41.446 | +| stdio digest | 0.000 | 0.000 | 0.001 | 0.001 | 0.001 | 0.008 | 15.187 | ## actiondfs Counters @@ -171,77 +186,77 @@ These `/proc/actiondfs_stats` counters cover the VM lifetime. | Counter | Value | | ----------------------------- | --------------: | -| mounts | 4,123 | +| mounts | 4,122 | | root directory parses | 1,406 | -| directory loads | 169,665 | -| cached directory requests | 169,665 | -| cached directory hits | 163,046 | -| cached directory misses | 6,619 | -| cached directory races | 0 | -| lookups | 1,407,098 | -| lookup hits | 842,187 | -| lookup negative | 564,911 | -| blob open attempts | 474,558 | +| directory loads | 169,617 | +| cached directory requests | 169,617 | +| cached directory hits | 163,009 | +| cached directory misses | 6,608 | +| cached directory races | 2 | +| lookups | 1,406,668 | +| lookup hits | 841,778 | +| lookup negative | 564,890 | +| blob open attempts | 474,182 | | blob open stale retries | 0 | -| blob-path cache hits | 460,970 | -| blob-path cache misses | 6,970 | +| blob-path cache hits | 460,692 | +| blob-path cache misses | 6,883 | | blob-path cache evictions | 0 | | blob-path cache insertion races | 0 | -| node blob cache hits | 472,567 | -| node blob cache misses | 467,940 | -| backing reads | 415,500 | -| backing read bytes | 1,303,975,362 | +| node blob cache hits | 472,190 | +| node blob cache misses | 467,575 | +| backing reads | 415,332 | +| backing read bytes | 1,303,012,282 | | backing read stale retries | 0 | -| mmap calls | 53,239 | -| mmap bytes | 1,901,676,589,056 | +| mmap calls | 53,030 | +| mmap bytes | 1,900,832,624,640 | | mmap failures | 0 | -| directory blob reads | 6,618 | -| directory blob bytes | 2,731,662 | +| directory blob reads | 6,607 | +| directory blob bytes | 2,720,991 | ## Staged Filesystem Counters | Counter | Value | | ------------------------------------ | ----------: | -| staged ensure-directory calls | 15,999 | +| staged ensure-directory calls | 15,996 | | staged ensure-directory components | 16 | | staged ensure-directory existing | 0 | | staged ensure-directory created | 16 | -| staged parent dentry hits | 15,983 | -| staged child lookups | 87,589 | -| staged child lookup hits | 35,540 | -| staged child lookup negative | 52,049 | +| staged parent dentry hits | 15,980 | +| staged child lookups | 87,570 | +| staged child lookup hits | 35,533 | +| staged child lookup negative | 52,037 | | staged child lookup failures | 0 | -| staged inode lookups | 1,407,098 | -| staged inode unstaged-parent skips | 1,331,722 | -| staged inode lookup hits | 35,524 | -| staged inode lookup negative | 39,852 | +| staged inode lookups | 1,406,668 | +| staged inode unstaged-parent skips | 1,331,308 | +| staged inode lookup hits | 35,518 | +| staged inode lookup negative | 39,842 | | staged inode lookup failures | 0 | -| staged inode input-directory merges | 18,388 | -| staged backing open attempts | 15,827 | +| staged inode input-directory merges | 18,382 | +| staged backing open attempts | 15,825 | | staged backing open failures | 0 | -| staged backing open total | 19.004 ms | -| staged backing open lookup | 0.296 ms | -| staged backing open file | 16.781 ms | -| staged create calls/successes | 11,983 | +| staged backing open total | 14.041 ms | +| staged backing open lookup | 0.295 ms | +| staged backing open file | 9.564 ms | +| staged create calls/successes | 11,981 | | staged create failures | 0 | | staged mkdir calls/successes | 198 | | staged mkdir failures | 0 | -| staged unlink calls/successes | 16 | +| staged unlink calls/successes | 15 | | staged unlink failures | 0 | -| staged rename calls/successes | 3,818 | +| staged rename calls/successes | 3,817 | | staged rename failures | 0 | -| staged size updates/successes | 3,875 | +| staged size updates/successes | 3,874 | | staged size update failures | 0 | -| staged write calls | 37,194 | -| staged write bytes | 106,356,427 | -| staged mmap calls | 31 | -| staged mmap bytes | 2,088,960 | +| staged write calls | 37,072 | +| staged write bytes | 105,802,304 | +| staged mmap calls | 30 | +| staged mmap bytes | 2,084,864 | | staged mmap failures | 0 | | staged `copy_file_range` successes | 3,828 | | staged `copy_file_range` bytes | 31,630,764 | | staged `copy_file_range` fallbacks | 0 | -The staged-negative filter skipped 1,331,722 lookups with known-unstaged +The staged-negative filter skipped 1,331,308 lookups with known-unstaged parents. All 3,828 `copy_file_range` attempts succeeded without a buffered fallback. @@ -249,14 +264,14 @@ fallback. | Counter | Value | | ------------------------------------ | ---------: | -| CAS put-file calls | 7,948 | -| CAS promotion attempts | 7,948 | -| CAS promotion success | 2,088 | +| CAS put-file calls | 7,947 | +| CAS promotion attempts | 7,947 | +| CAS promotion success | 2,087 | | CAS existing-blob hits | 5,860 | -| CAS promoted bytes | 32,204,511 | -| CAS digest bytes | 84,538,463 | -| CAS promotion digest | 686.880 ms | -| CAS promotion rename | 142.628 ms | +| CAS promoted bytes | 31,753,839 | +| CAS digest bytes | 84,087,791 | +| CAS promotion digest | 848.446 ms | +| CAS promotion rename | 165.918 ms | | CAS cross-device fallbacks | 0 | | CAS permission fallbacks | 0 | | CAS put-file copy calls | 0 | @@ -265,19 +280,19 @@ fallback. | Counter | Value | | ------------------------------------ | ---------: | -| ByteStream file reads | 4,039 | -| ByteStream file bytes | 57,718,456 | +| ByteStream file reads | 4,038 | +| ByteStream file bytes | 57,615,015 | | ByteStream buffered reads | 0 | -| gRPC response tasks started | 36,254 | +| gRPC response tasks started | 36,229 | | gRPC response tasks failed | 0 | | gRPC response concurrency waits | 0 | -| gRPC data frames | 37,041 | -| gRPC file payload frames | 6,909 | -| gRPC `sendfile` attempts/successes | 6,909 | -| gRPC `sendfile` bytes | 57,718,456 | +| gRPC data frames | 37,011 | +| gRPC file payload frames | 6,902 | +| gRPC `sendfile` attempts/successes | 6,902 | +| gRPC `sendfile` bytes | 57,615,015 | | gRPC `sendfile` fallbacks | 0 | -The measured TCP-to-vsock bridge logged two connections, 28.29 MiB from client +The measured TCP-to-vsock bridge logged two connections, 28.18 MiB from client to guest, 37.37 MiB from guest to client, and zero read/write pump errors. -ByteStream reads remained file-backed and all 6,909 gRPC `sendfile` attempts +ByteStream reads remained file-backed and all 6,902 gRPC `sendfile` attempts succeeded. diff --git a/kernel/actiondfs/actiondfs.c b/kernel/actiondfs/actiondfs.c index 0875b2d..fa0fe6c 100644 --- a/kernel/actiondfs/actiondfs.c +++ b/kernel/actiondfs/actiondfs.c @@ -2270,8 +2270,10 @@ static int actiondfs_set_input_inode(struct inode *inode, void *data) static void actiondfs_init_inode(struct inode *inode, struct actiondfs_node *node) { - if (!node->input_child) + if (!node->input_child) { inode->i_ino = node->ino; + inode->i_private = node; + } if (node->stage_dentry) { inode->i_mode = node->mode; actiondfs_copy_stage_owner(inode, d_inode(node->stage_dentry)); @@ -2280,7 +2282,6 @@ static void actiondfs_init_inode(struct inode *inode, current_fsuid_fsgid(&inode->i_uid, &inode->i_gid); } inode_has_no_xattr(inode); - inode->i_private = node; if (node->input_child) { struct inode *root = d_inode(inode->i_sb->s_root); diff --git a/test/STRESS_TIMINGS.md b/test/STRESS_TIMINGS.md index c30d562..cc61202 100644 --- a/test/STRESS_TIMINGS.md +++ b/test/STRESS_TIMINGS.md @@ -1,10 +1,10 @@ # Executor Timing Summary -- Generated: `2026-08-01 23:08:06 EDT` +- Generated: `2026-08-02 17:31:16 EDT` - Mode: `vm` - Execute records parsed: `37` - Unique action digests: `37` -- Source log: `/private/var/folders/p4/xn8y5q_j24l5xwgwd_jx5c340000gn/T/actiond-vm-e2e.d5iHEX/darwin-actiond-vm.log` +- Source log: `/var/folders/p4/xn8y5q_j24l5xwgwd_jx5c340000gn/T/actiond-vm-e2e.ZrWA2N/darwin-actiond-vm.log` - Command: `ACTIOND_E2E_KEEP_TMP=1 ACTIOND_REPO_BAZEL_FLAGS="--config=remote --config=executor_timing_logs" tools/e2e.sh vm` ## Stage Timing @@ -13,10 +13,10 @@ All timing values are milliseconds unless noted. | Stage | Min | p25 | p50 | p75 | p95 | Mean | Max | Share of summed total | | --------------------- | ----: | -----: | -----: | -----: | -----: | -----: | ------: | --------------------: | -| total | 1.886 | 12.985 | 17.967 | 26.434 | 54.587 | 23.937 | 105.540 | 100.0% | -| input fetch/setup | 0.232 | 0.321 | 0.399 | 0.697 | 4.007 | 1.078 | 9.242 | 4.5% | -| execute | 1.538 | 10.747 | 16.541 | 24.216 | 51.809 | 21.702 | 102.521 | 90.7% | -| output upload/collect | 0.060 | 0.139 | 0.486 | 1.117 | 4.199 | 1.157 | 10.068 | 4.8% | +| total | 1.952 | 12.928 | 22.751 | 32.288 | 65.471 | 29.264 | 213.224 | 100.0% | +| input fetch/setup | 0.198 | 0.291 | 0.319 | 0.530 | 1.456 | 0.897 | 16.614 | 3.1% | +| execute | 1.644 | 12.424 | 19.441 | 31.322 | 62.674 | 26.891 | 212.333 | 91.9% | +| output upload/collect | 0.056 | 0.156 | 0.388 | 0.603 | 3.684 | 1.476 | 28.829 | 5.0% | ## Mount And Output Counts @@ -30,20 +30,20 @@ All timing values are milliseconds unless noted. | Stress Case | Actions | Total p25 | Total p50 | Total p75 | Total p95 | Input p50 | Execute p50 | Output p50 | Mounts p50 | | -------------------------- | ------: | --------: | --------: | --------: | --------: | --------: | ----------: | ---------: | ---------: | -| aggregate | 1 | 12.985 | 12.985 | 12.985 | 12.985 | 0.300 | 12.625 | 0.060 | 4 | -| bare_individual_files | 4 | 22.180 | 25.206 | 27.570 | 28.142 | 0.406 | 23.426 | 0.368 | 4 | -| filesystem_regression | 1 | 105.540 | 105.540 | 105.540 | 105.540 | 0.947 | 102.521 | 2.071 | 4 | -| generated_file_producer | 1 | 42.392 | 42.392 | 42.392 | 42.392 | 9.242 | 24.742 | 8.408 | 4 | -| generated_individual_files | 4 | 10.012 | 10.295 | 12.001 | 15.814 | 0.323 | 9.114 | 0.443 | 4 | -| generated_tree_producer | 1 | 45.624 | 45.624 | 45.624 | 45.624 | 0.377 | 35.180 | 10.068 | 4 | -| generated_tree_reuse | 8 | 15.657 | 18.662 | 21.268 | 22.079 | 0.580 | 17.102 | 0.213 | 4 | -| mixed_all | 1 | 12.815 | 12.815 | 12.815 | 12.815 | 0.321 | 10.046 | 2.448 | 4 | -| nested_individual_files | 8 | 16.920 | 19.062 | 29.772 | 39.481 | 0.455 | 16.524 | 0.784 | 4 | -| pids_one_regression | 1 | 16.760 | 16.760 | 16.760 | 16.760 | 1.012 | 15.606 | 0.142 | 4 | -| source_dir_tree | 4 | 16.374 | 22.200 | 42.434 | 80.835 | 0.553 | 21.679 | 0.166 | 4 | -| symlink_input_consumer | 1 | 5.609 | 5.609 | 5.609 | 5.609 | 0.331 | 5.061 | 0.217 | 4 | -| timeout_recovery | 1 | 22.593 | 22.593 | 22.593 | 22.593 | 0.643 | 21.843 | 0.107 | 4 | -| unknown | 1 | 1.886 | 1.886 | 1.886 | 1.886 | 0.232 | 1.538 | 0.117 | 2 | +| aggregate | 1 | 31.942 | 31.942 | 31.942 | 31.942 | 0.281 | 31.606 | 0.056 | 4 | +| bare_individual_files | 4 | 11.904 | 16.790 | 30.748 | 56.390 | 0.377 | 16.248 | 0.274 | 4 | +| filesystem_regression | 1 | 213.224 | 213.224 | 213.224 | 213.224 | 0.287 | 212.333 | 0.603 | 4 | +| generated_file_producer | 1 | 24.952 | 24.952 | 24.952 | 24.952 | 0.692 | 16.875 | 7.385 | 4 | +| generated_individual_files | 4 | 5.633 | 6.113 | 7.625 | 10.553 | 0.313 | 5.662 | 0.257 | 4 | +| generated_tree_producer | 1 | 47.602 | 47.602 | 47.602 | 47.602 | 0.317 | 18.456 | 28.829 | 4 | +| generated_tree_reuse | 8 | 21.052 | 22.670 | 29.163 | 30.309 | 0.323 | 22.179 | 0.201 | 4 | +| mixed_all | 1 | 34.436 | 34.436 | 34.436 | 34.436 | 0.303 | 31.471 | 2.662 | 4 | +| nested_individual_files | 8 | 32.254 | 34.462 | 35.784 | 62.467 | 0.319 | 32.381 | 0.535 | 4 | +| pids_one_regression | 1 | 16.626 | 16.626 | 16.626 | 16.626 | 0.584 | 15.768 | 0.274 | 4 | +| source_dir_tree | 4 | 10.899 | 13.767 | 18.627 | 26.835 | 0.421 | 12.867 | 0.370 | 4 | +| symlink_input_consumer | 1 | 6.269 | 6.269 | 6.269 | 6.269 | 1.013 | 5.039 | 0.217 | 4 | +| timeout_recovery | 1 | 16.611 | 16.611 | 16.611 | 16.611 | 0.364 | 16.141 | 0.105 | 4 | +| unknown | 1 | 1.952 | 1.952 | 1.952 | 1.952 | 0.198 | 1.644 | 0.110 | 2 | ## Visible Overhead Estimate @@ -51,122 +51,122 @@ All timing values are milliseconds unless noted. | Metric | Min | p25 | p50 | p75 | p95 | Mean | Max | Share of summed total | | ------------------------- | ----: | ----: | -----: | -----: | -----: | -----: | -----: | --------------------: | -| fixed overhead, no wait | 1.394 | 8.799 | 13.486 | 19.203 | 33.924 | 16.445 | 76.739 | 68.7% | -| fixed overhead, with wait | 1.394 | 8.799 | 15.393 | 21.165 | 35.607 | 17.050 | 76.739 | 71.2% | +| fixed overhead, no wait | 1.601 | 9.403 | 19.053 | 26.967 | 33.695 | 18.168 | 40.780 | 62.1% | +| fixed overhead, with wait | 1.601 | 9.403 | 19.053 | 26.967 | 33.695 | 18.327 | 40.780 | 62.6% | ## Runner Timing These values split the `execute` bucket. `child setup` includes successful `execve`; `process/io` starts after close-on-exec confirmation and includes action runtime and stdout/stderr drain. -| Runner Stage | Min | p25 | p50 | p75 | p95 | Mean | Max | Share of execute | -| -------------- | ----: | ----: | ----: | -----: | -----: | ----: | -----: | ---------------: | -| parent prepare | 0.039 | 0.129 | 0.154 | 6.001 | 14.192 | 3.974 | 30.510 | 18.3% | -| fork | 0.143 | 0.346 | 0.463 | 1.037 | 5.559 | 1.483 | 15.454 | 6.8% | -| child setup | 0.008 | 1.626 | 5.090 | 11.872 | 19.394 | 8.753 | 74.287 | 40.3% | -| process/io | 0.474 | 2.012 | 3.644 | 5.844 | 14.223 | 6.837 | 83.844 | 31.5% | -| wait | 0.000 | 0.000 | 0.000 | 0.000 | 4.609 | 0.605 | 4.742 | 2.8% | -| stdio digest | 0.000 | 0.000 | 0.000 | 0.001 | 0.001 | 0.000 | 0.003 | 0.0% | - -| Stress Case | Digest | Parent Prep | Fork | Child Setup | Process/IO | Wait | Stdio Digest | Setup Signaled | -| -------------------------- | -------------- | ----------: | -----: | ----------: | ---------: | ----: | -----------: | -------------- | -| aggregate | `b685ab4a0d00` | 0.110 | 0.276 | 9.863 | 2.348 | 0.000 | 0.001 | True | -| bare_individual_files | `24c5cd3f112a` | 13.991 | 0.348 | 4.009 | 5.844 | 0.000 | 0.000 | True | -| bare_individual_files | `2623700af2b1` | 0.146 | 0.463 | 14.002 | 12.122 | 0.000 | 0.001 | True | -| bare_individual_files | `43f037c27458` | 14.996 | 0.722 | 3.040 | 1.851 | 1.962 | 0.000 | True | -| bare_individual_files | `feeb2d55641f` | 0.122 | 0.283 | 7.266 | 10.812 | 0.000 | 0.001 | True | -| filesystem_regression | `d132925a0328` | 0.123 | 3.175 | 15.351 | 83.844 | 0.000 | 0.000 | True | -| generated_file_producer | `e84ec75ce42e` | 0.154 | 8.245 | 5.814 | 8.351 | 2.104 | 0.000 | True | -| generated_individual_files | `118b63159b38` | 4.064 | 1.021 | 0.886 | 2.487 | 0.000 | 0.000 | True | -| generated_individual_files | `242c99eb2c28` | 2.561 | 1.778 | 3.018 | 2.371 | 0.000 | 0.000 | True | -| generated_individual_files | `78230e4a6f6d` | 3.444 | 1.892 | 3.274 | 7.660 | 0.000 | 0.000 | True | -| generated_individual_files | `ec087db43ffd` | 3.041 | 2.295 | 0.877 | 1.571 | 0.000 | 0.001 | True | -| generated_tree_producer | `d33821cebf3e` | 0.133 | 0.346 | 18.122 | 16.559 | 0.000 | 0.001 | True | -| generated_tree_reuse | `0bbda7aff823` | 10.205 | 0.554 | 2.021 | 2.012 | 0.000 | 0.000 | True | -| generated_tree_reuse | `28f302b72b2d` | 9.988 | 0.622 | 0.918 | 2.879 | 0.000 | 0.000 | True | -| generated_tree_reuse | `3fa11b1bc05f` | 9.799 | 0.394 | 1.626 | 4.833 | 4.586 | 0.000 | True | -| generated_tree_reuse | `63a12c93411c` | 0.122 | 0.447 | 1.838 | 4.593 | 0.000 | 0.000 | True | -| generated_tree_reuse | `654f662800a0` | 10.530 | 1.287 | 0.008 | 4.334 | 0.000 | 0.000 | True | -| generated_tree_reuse | `740cb972a7d0` | 0.143 | 0.528 | 13.035 | 4.378 | 0.000 | 0.000 | True | -| generated_tree_reuse | `9de4f80099e4` | 10.471 | 0.987 | 0.460 | 2.529 | 4.699 | 0.000 | True | -| generated_tree_reuse | `ce4b28b15b03` | 0.135 | 0.581 | 11.689 | 5.576 | 0.000 | 0.000 | True | -| mixed_all | `05385b608139` | 0.131 | 0.318 | 1.045 | 8.530 | 0.000 | 0.001 | True | -| nested_individual_files | `0576f836a969` | 0.129 | 2.737 | 13.501 | 8.299 | 4.307 | 0.000 | True | -| nested_individual_files | `0ba5a66718e5` | 10.286 | 0.503 | 3.945 | 1.783 | 0.000 | 0.000 | True | -| nested_individual_files | `1dddb1244c87` | 0.100 | 0.288 | 11.872 | 1.837 | 0.000 | 0.003 | True | -| nested_individual_files | `5043fa575aa6` | 0.140 | 0.233 | 2.211 | 1.442 | 4.742 | 0.000 | True | -| nested_individual_files | `79f9b55d3fb8` | 0.181 | 0.410 | 10.182 | 5.684 | 0.000 | 0.001 | True | -| nested_individual_files | `c10c168c3ea7` | 3.846 | 0.414 | 1.603 | 4.816 | 0.000 | 0.000 | True | -| nested_individual_files | `df42d8f5a591` | 6.001 | 0.455 | 20.712 | 1.628 | 0.000 | 0.000 | True | -| nested_individual_files | `f1239aa39480` | 30.510 | 0.444 | 9.207 | 2.468 | 0.000 | 0.000 | True | -| pids_one_regression | `f4c69ffb2998` | 0.294 | 0.354 | 13.795 | 1.119 | 0.000 | 0.001 | True | -| source_dir_tree | `280e6ad6e0fe` | 0.120 | 4.888 | 9.557 | 2.830 | 0.000 | 0.000 | True | -| source_dir_tree | `35fb441c98d9` | 0.442 | 1.037 | 5.090 | 4.158 | 0.000 | 0.000 | True | -| source_dir_tree | `685a1ba5f47a` | 0.117 | 0.262 | 74.287 | 13.639 | 0.000 | 0.001 | True | -| source_dir_tree | `6a2a37eadcc5` | 0.120 | 15.454 | 8.903 | 1.408 | 0.000 | 0.001 | True | -| symlink_input_consumer | `2698574104c6` | 0.144 | 0.345 | 0.904 | 3.644 | 0.000 | 0.000 | True | -| timeout_recovery | `dc6f23f617c1` | 0.148 | 0.336 | 19.064 | 2.254 | 0.000 | 0.001 | True | -| unknown | `9d7a2c7012de` | 0.039 | 0.143 | 0.863 | 0.474 | 0.000 | 0.000 | True | +| Runner Stage | Min | p25 | p50 | p75 | p95 | Mean | Max | Share of execute | +| -------------- | ----: | ----: | ----: | -----: | -----: | -----: | ------: | ---------------: | +| parent prepare | 0.029 | 0.126 | 0.348 | 8.996 | 24.580 | 5.820 | 25.331 | 21.6% | +| fork | 0.102 | 0.252 | 0.317 | 0.564 | 3.637 | 0.803 | 5.233 | 3.0% | +| child setup | 0.692 | 2.342 | 4.242 | 14.884 | 27.495 | 9.172 | 31.297 | 34.1% | +| process/io | 0.341 | 1.577 | 3.476 | 5.434 | 35.588 | 10.905 | 191.373 | 40.6% | +| wait | 0.000 | 0.000 | 0.000 | 0.000 | 0.489 | 0.159 | 3.426 | 0.6% | +| stdio digest | 0.000 | 0.000 | 0.000 | 0.000 | 0.001 | 0.000 | 0.001 | 0.0% | + +| Stress Case | Digest | Parent Prep | Fork | Child Setup | Process/IO | Wait | Stdio Digest | Setup Signaled | +| -------------------------- | -------------- | ----------: | ----: | ----------: | ---------: | ----: | -----------: | -------------- | +| aggregate | `b685ab4a0d00` | 0.103 | 0.264 | 29.013 | 2.204 | 0.000 | 0.001 | True | +| bare_individual_files | `24c5cd3f112a` | 0.143 | 0.317 | 12.070 | 6.888 | 0.000 | 0.000 | True | +| bare_individual_files | `2623700af2b1` | 21.587 | 0.442 | 3.262 | 34.173 | 0.000 | 0.000 | True | +| bare_individual_files | `43f037c27458` | 0.162 | 0.476 | 2.342 | 3.333 | 0.000 | 0.000 | True | +| bare_individual_files | `feeb2d55641f` | 8.996 | 0.564 | 2.096 | 1.379 | 0.000 | 0.000 | True | +| filesystem_regression | `d132925a0328` | 16.896 | 0.203 | 3.831 | 191.373 | 0.000 | 0.000 | True | +| generated_file_producer | `e84ec75ce42e` | 8.042 | 0.944 | 2.454 | 5.417 | 0.000 | 0.000 | True | +| generated_individual_files | `118b63159b38` | 0.105 | 0.292 | 3.474 | 3.257 | 3.426 | 0.000 | True | +| generated_individual_files | `242c99eb2c28` | 0.126 | 0.369 | 1.564 | 1.365 | 0.000 | 0.000 | True | +| generated_individual_files | `78230e4a6f6d` | 0.142 | 0.496 | 2.165 | 3.092 | 0.000 | 0.000 | True | +| generated_individual_files | `ec087db43ffd` | 0.094 | 0.234 | 4.570 | 0.485 | 0.000 | 0.000 | True | +| generated_tree_producer | `d33821cebf3e` | 8.290 | 0.320 | 3.024 | 6.809 | 0.000 | 0.000 | True | +| generated_tree_reuse | `0bbda7aff823` | 15.067 | 0.185 | 0.692 | 6.349 | 0.000 | 0.000 | True | +| generated_tree_reuse | `28f302b72b2d` | 0.208 | 3.427 | 17.194 | 5.434 | 0.000 | 0.000 | True | +| generated_tree_reuse | `3fa11b1bc05f` | 14.356 | 0.313 | 3.245 | 3.499 | 0.000 | 0.000 | True | +| generated_tree_reuse | `63a12c93411c` | 0.120 | 0.277 | 27.116 | 1.424 | 0.000 | 0.001 | True | +| generated_tree_reuse | `654f662800a0` | 2.987 | 2.462 | 3.306 | 3.652 | 0.000 | 0.000 | True | +| generated_tree_reuse | `740cb972a7d0` | 11.111 | 0.432 | 1.176 | 3.440 | 0.000 | 0.000 | True | +| generated_tree_reuse | `9de4f80099e4` | 0.108 | 5.233 | 19.376 | 5.056 | 0.000 | 0.000 | True | +| generated_tree_reuse | `ce4b28b15b03` | 15.120 | 2.556 | 2.024 | 2.332 | 0.000 | 0.000 | True | +| mixed_all | `05385b608139` | 3.163 | 0.603 | 5.548 | 22.133 | 0.000 | 0.001 | True | +| nested_individual_files | `0576f836a969` | 24.816 | 0.217 | 6.841 | 1.466 | 0.000 | 0.000 | True | +| nested_individual_files | `0ba5a66718e5` | 25.331 | 0.224 | 8.487 | 41.246 | 0.000 | 0.000 | True | +| nested_individual_files | `1dddb1244c87` | 0.111 | 0.246 | 25.783 | 5.157 | 0.000 | 0.001 | True | +| nested_individual_files | `5043fa575aa6` | 0.627 | 0.241 | 12.449 | 3.724 | 0.000 | 0.001 | True | +| nested_individual_files | `79f9b55d3fb8` | 3.979 | 0.261 | 19.055 | 11.193 | 0.000 | 0.000 | True | +| nested_individual_files | `c10c168c3ea7` | 0.133 | 0.257 | 31.297 | 3.615 | 0.000 | 0.000 | True | +| nested_individual_files | `df42d8f5a591` | 0.127 | 4.476 | 18.086 | 1.432 | 0.000 | 0.000 | True | +| nested_individual_files | `f1239aa39480` | 24.521 | 0.356 | 3.015 | 3.476 | 0.000 | 0.001 | True | +| pids_one_regression | `f4c69ffb2998` | 0.225 | 0.306 | 13.631 | 1.577 | 0.000 | 0.001 | True | +| source_dir_tree | `280e6ad6e0fe` | 7.308 | 0.876 | 4.242 | 2.107 | 0.000 | 0.000 | True | +| source_dir_tree | `35fb441c98d9` | 0.191 | 0.252 | 1.103 | 4.111 | 0.000 | 0.000 | True | +| source_dir_tree | `685a1ba5f47a` | 0.348 | 0.601 | 7.307 | 2.873 | 0.000 | 0.000 | True | +| source_dir_tree | `6a2a37eadcc5` | 0.108 | 0.349 | 21.562 | 6.323 | 0.000 | 0.000 | True | +| symlink_input_consumer | `2698574104c6` | 0.443 | 0.280 | 0.934 | 0.900 | 2.446 | 0.000 | True | +| timeout_recovery | `dc6f23f617c1` | 0.120 | 0.252 | 14.884 | 0.858 | 0.000 | 0.000 | True | +| unknown | `9d7a2c7012de` | 0.029 | 0.102 | 1.162 | 0.341 | 0.000 | 0.000 | True | ## VM Bridge Timing These are raw TCP-to-vsock pump connection measurements logged by `darwin-actiond serve-vm`. -- Bridge connections logged: `14` -- Total client to guest bytes: `1.51 MiB` +- Bridge connections logged: `10` +- Total client to guest bytes: `1.48 MiB` - Total guest to client bytes: `0.30 MiB` - Pump errors: read=`0`, write=`0` -| Bridge Metric | Min | p25 | p50 | p75 | p95 | Mean | Max | -| ---------------------- | ------: | -------: | -------: | -------: | -------: | -------: | -------: | -| connection elapsed | 242.059 | 1063.933 | 1132.733 | 2243.634 | 5052.708 | 1803.113 | 5109.920 | -| client to guest KiB | 0.0 | 30.9 | 79.8 | 122.4 | 317.5 | 110.6 | 587.5 | -| guest to client KiB | 0.0 | 2.2 | 18.1 | 20.3 | 74.8 | 22.1 | 114.3 | -| client to guest reads | 1 | 13 | 112 | 161 | 202 | 100.6 | 212 | -| client to guest writes | 1 | 13 | 112 | 161 | 202 | 100.6 | 212 | -| guest to client reads | 0 | 14 | 62 | 78 | 116 | 57.0 | 147 | -| guest to client writes | 0 | 14 | 62 | 78 | 116 | 57.0 | 147 | +| Bridge Metric | Min | p25 | p50 | p75 | p95 | Mean | Max | +| ---------------------- | ------: | ------: | ------: | -------: | -------: | -------: | -------: | +| connection elapsed | 168.522 | 743.358 | 871.378 | 2786.821 | 5127.514 | 1885.829 | 5297.803 | +| client to guest KiB | 0.0 | 19.6 | 154.6 | 178.9 | 421.0 | 151.0 | 587.5 | +| guest to client KiB | 0.0 | 1.3 | 20.2 | 55.5 | 82.8 | 30.9 | 101.9 | +| client to guest reads | 1 | 12 | 58 | 224 | 316 | 118.1 | 339 | +| client to guest writes | 1 | 12 | 58 | 224 | 316 | 118.1 | 339 | +| guest to client reads | 0 | 10 | 84 | 197 | 207 | 99.0 | 212 | +| guest to client writes | 0 | 10 | 84 | 197 | 207 | 99.0 | 212 | ## Per Action -| Stress Case | Digest | Total | Input | Execute | Output | Bind Mounts | Outputs | -| -------------------------- | -------------- | ------: | ----: | ------: | -----: | ----------: | -------------: | -| aggregate | `b685ab4a0d00` | 12.985 | 0.300 | 12.625 | 0.060 | 4 | 1 file, 0 dir | -| bare_individual_files | `24c5cd3f112a` | 27.332 | 2.971 | 24.216 | 0.144 | 4 | 1 file, 1 dir | -| bare_individual_files | `2623700af2b1` | 28.285 | 0.413 | 26.756 | 1.117 | 4 | 1 file, 1 dir | -| bare_individual_files | `43f037c27458` | 23.081 | 0.283 | 22.636 | 0.161 | 4 | 1 file, 1 dir | -| bare_individual_files | `feeb2d55641f` | 19.477 | 0.399 | 18.503 | 0.575 | 4 | 1 file, 1 dir | -| filesystem_regression | `d132925a0328` | 105.540 | 0.947 | 102.521 | 2.071 | 4 | 1 file, 1 dir | -| generated_file_producer | `e84ec75ce42e` | 42.392 | 9.242 | 24.742 | 8.408 | 4 | 97 file, 0 dir | -| generated_individual_files | `118b63159b38` | 9.516 | 0.276 | 8.485 | 0.755 | 4 | 1 file, 1 dir | -| generated_individual_files | `242c99eb2c28` | 10.178 | 0.303 | 9.743 | 0.131 | 4 | 1 file, 1 dir | -| generated_individual_files | `78230e4a6f6d` | 16.768 | 0.343 | 16.294 | 0.131 | 4 | 1 file, 1 dir | -| generated_individual_files | `ec087db43ffd` | 10.412 | 0.536 | 7.827 | 2.049 | 4 | 1 file, 1 dir | -| generated_tree_producer | `d33821cebf3e` | 45.624 | 0.377 | 35.180 | 10.068 | 4 | 1 file, 1 dir | -| generated_tree_reuse | `0bbda7aff823` | 15.830 | 0.596 | 15.124 | 0.110 | 4 | 1 file, 1 dir | -| generated_tree_reuse | `28f302b72b2d` | 15.136 | 0.289 | 14.735 | 0.113 | 4 | 1 file, 1 dir | -| generated_tree_reuse | `3fa11b1bc05f` | 22.070 | 0.697 | 21.263 | 0.110 | 4 | 1 file, 1 dir | -| generated_tree_reuse | `63a12c93411c` | 7.636 | 0.313 | 7.021 | 0.302 | 4 | 1 file, 1 dir | -| generated_tree_reuse | `654f662800a0` | 17.689 | 0.564 | 16.192 | 0.933 | 4 | 1 file, 1 dir | -| generated_tree_reuse | `740cb972a7d0` | 22.083 | 3.158 | 18.115 | 0.810 | 4 | 1 file, 1 dir | -| generated_tree_reuse | `9de4f80099e4` | 19.634 | 0.324 | 19.186 | 0.124 | 4 | 1 file, 1 dir | -| generated_tree_reuse | `ce4b28b15b03` | 21.001 | 2.270 | 18.013 | 0.718 | 4 | 1 file, 1 dir | -| mixed_all | `05385b608139` | 12.815 | 0.321 | 10.046 | 2.448 | 4 | 1 file, 1 dir | -| nested_individual_files | `0576f836a969` | 29.748 | 0.345 | 28.995 | 0.408 | 4 | 1 file, 1 dir | -| nested_individual_files | `0ba5a66718e5` | 17.951 | 0.542 | 16.541 | 0.869 | 4 | 1 file, 1 dir | -| nested_individual_files | `1dddb1244c87` | 14.939 | 0.321 | 14.131 | 0.486 | 4 | 1 file, 1 dir | -| nested_individual_files | `5043fa575aa6` | 17.580 | 7.401 | 8.830 | 1.349 | 4 | 1 file, 1 dir | -| nested_individual_files | `79f9b55d3fb8` | 20.173 | 0.519 | 16.507 | 3.147 | 4 | 1 file, 1 dir | -| nested_individual_files | `c10c168c3ea7` | 11.994 | 0.608 | 10.722 | 0.663 | 4 | 1 file, 1 dir | -| nested_individual_files | `df42d8f5a591` | 29.843 | 0.310 | 28.834 | 0.698 | 4 | 1 file, 1 dir | -| nested_individual_files | `f1239aa39480` | 44.671 | 0.391 | 42.671 | 1.609 | 4 | 1 file, 1 dir | -| pids_one_regression | `f4c69ffb2998` | 16.760 | 1.012 | 15.606 | 0.142 | 4 | 1 file, 1 dir | -| source_dir_tree | `280e6ad6e0fe` | 17.967 | 0.394 | 17.426 | 0.147 | 4 | 1 file, 1 dir | -| source_dir_tree | `35fb441c98d9` | 11.597 | 0.712 | 10.747 | 0.139 | 4 | 1 file, 1 dir | -| source_dir_tree | `685a1ba5f47a` | 90.436 | 0.897 | 88.363 | 1.175 | 4 | 1 file, 1 dir | -| source_dir_tree | `6a2a37eadcc5` | 26.434 | 0.316 | 25.931 | 0.186 | 4 | 1 file, 1 dir | -| symlink_input_consumer | `2698574104c6` | 5.609 | 0.331 | 5.061 | 0.217 | 4 | 1 file, 1 dir | -| timeout_recovery | `dc6f23f617c1` | 22.593 | 0.643 | 21.843 | 0.107 | 4 | 1 file, 1 dir | -| unknown | `9d7a2c7012de` | 1.886 | 0.232 | 1.538 | 0.117 | 2 | 1 file, 0 dir | +| Stress Case | Digest | Total | Input | Execute | Output | Bind Mounts | Outputs | +| -------------------------- | -------------- | ------: | -----: | ------: | -----: | ----------: | -------------: | +| aggregate | `b685ab4a0d00` | 31.942 | 0.281 | 31.606 | 0.056 | 4 | 1 file, 0 dir | +| bare_individual_files | `24c5cd3f112a` | 20.064 | 0.304 | 19.441 | 0.319 | 4 | 1 file, 1 dir | +| bare_individual_files | `2623700af2b1` | 62.800 | 0.530 | 59.512 | 2.758 | 4 | 1 file, 1 dir | +| bare_individual_files | `43f037c27458` | 7.068 | 0.417 | 6.422 | 0.228 | 4 | 1 file, 1 dir | +| bare_individual_files | `feeb2d55641f` | 13.516 | 0.337 | 13.054 | 0.125 | 4 | 1 file, 1 dir | +| filesystem_regression | `d132925a0328` | 213.224 | 0.287 | 212.333 | 0.603 | 4 | 1 file, 1 dir | +| generated_file_producer | `e84ec75ce42e` | 24.952 | 0.692 | 16.875 | 7.385 | 4 | 97 file, 0 dir | +| generated_individual_files | `118b63159b38` | 11.285 | 0.319 | 10.582 | 0.384 | 4 | 1 file, 1 dir | +| generated_individual_files | `242c99eb2c28` | 5.068 | 0.307 | 3.507 | 1.254 | 4 | 1 file, 1 dir | +| generated_individual_files | `78230e4a6f6d` | 6.406 | 0.354 | 5.920 | 0.131 | 4 | 1 file, 1 dir | +| generated_individual_files | `ec087db43ffd` | 5.821 | 0.291 | 5.404 | 0.125 | 4 | 1 file, 1 dir | +| generated_tree_producer | `d33821cebf3e` | 47.602 | 0.317 | 18.456 | 28.829 | 4 | 1 file, 1 dir | +| generated_tree_reuse | `0bbda7aff823` | 22.751 | 0.347 | 22.309 | 0.096 | 4 | 1 file, 1 dir | +| generated_tree_reuse | `28f302b72b2d` | 29.034 | 1.997 | 26.309 | 0.728 | 4 | 1 file, 1 dir | +| generated_tree_reuse | `3fa11b1bc05f` | 22.588 | 0.299 | 21.449 | 0.839 | 4 | 1 file, 1 dir | +| generated_tree_reuse | `63a12c93411c` | 29.550 | 0.430 | 28.964 | 0.156 | 4 | 1 file, 1 dir | +| generated_tree_reuse | `654f662800a0` | 12.928 | 0.257 | 12.424 | 0.247 | 4 | 1 file, 1 dir | +| generated_tree_reuse | `740cb972a7d0` | 16.970 | 0.285 | 16.251 | 0.433 | 4 | 1 file, 1 dir | +| generated_tree_reuse | `9de4f80099e4` | 30.717 | 0.808 | 29.798 | 0.112 | 4 | 1 file, 1 dir | +| generated_tree_reuse | `ce4b28b15b03` | 22.412 | 0.260 | 22.050 | 0.102 | 4 | 1 file, 1 dir | +| mixed_all | `05385b608139` | 34.436 | 0.303 | 31.471 | 2.662 | 4 | 1 file, 1 dir | +| nested_individual_files | `0576f836a969` | 34.112 | 0.322 | 33.364 | 0.426 | 4 | 1 file, 1 dir | +| nested_individual_files | `0ba5a66718e5` | 76.157 | 0.370 | 75.323 | 0.463 | 4 | 1 file, 1 dir | +| nested_individual_files | `1dddb1244c87` | 32.149 | 0.295 | 31.322 | 0.532 | 4 | 1 file, 1 dir | +| nested_individual_files | `5043fa575aa6` | 34.813 | 16.614 | 17.074 | 1.125 | 4 | 1 file, 1 dir | +| nested_individual_files | `79f9b55d3fb8` | 35.365 | 0.317 | 34.510 | 0.538 | 4 | 1 file, 1 dir | +| nested_individual_files | `c10c168c3ea7` | 37.042 | 1.321 | 35.330 | 0.392 | 4 | 1 file, 1 dir | +| nested_individual_files | `df42d8f5a591` | 25.220 | 0.288 | 24.142 | 0.790 | 4 | 1 file, 1 dir | +| nested_individual_files | `f1239aa39480` | 32.288 | 0.290 | 31.398 | 0.600 | 4 | 1 file, 1 dir | +| pids_one_regression | `f4c69ffb2998` | 16.626 | 0.584 | 15.768 | 0.274 | 4 | 1 file, 1 dir | +| source_dir_tree | `280e6ad6e0fe` | 15.207 | 0.299 | 14.557 | 0.351 | 4 | 1 file, 1 dir | +| source_dir_tree | `35fb441c98d9` | 6.617 | 0.543 | 5.685 | 0.388 | 4 | 1 file, 1 dir | +| source_dir_tree | `685a1ba5f47a` | 12.326 | 0.660 | 11.178 | 0.487 | 4 | 1 file, 1 dir | +| source_dir_tree | `6a2a37eadcc5` | 28.887 | 0.291 | 28.367 | 0.228 | 4 | 1 file, 1 dir | +| symlink_input_consumer | `2698574104c6` | 6.269 | 1.013 | 5.039 | 0.217 | 4 | 1 file, 1 dir | +| timeout_recovery | `dc6f23f617c1` | 16.611 | 0.364 | 16.141 | 0.105 | 4 | 1 file, 1 dir | +| unknown | `9d7a2c7012de` | 1.952 | 0.198 | 1.644 | 0.110 | 2 | 1 file, 0 dir | ## Notes