It seems to be some memory management issue; that's the usual cause for the BUSY message. For some reason, there is memory allocated from the SRM buffer, that has to be freed before the camera is able to take another picture (that's a limitation from Canon firmware).
Here's what I did to narrow it down:
1) some changes in mem.c to log the activity of "large" memory allocators (shoot_malloc and srm_malloc, or anything else other than the general-purpose ones):
diff -r 75c1f3fe2927 src/mem.c
--- a/src/mem.c
+++ b/src/mem.c
@@ -30,2 +30,3 @@
#endif
+#define exm_printf(fmt,...) { if (allocator_index > 2) printf(fmt, ## __VA_ARGS__); }
@@ -811,3 +812,4 @@
- dbg_printf("using %s (%d blocks, %x free, %x max region)\n",
+ exm_printf("alloc(%s) from %s:%d task %s\n", format_memory_size_and_flags(size, flags), file, line, current_task->name);
+ exm_printf("using %s (%d blocks, %x free, %x max region)\n",
allocators[allocator_index].name,
@@ -875,3 +877,3 @@
- dbg_printf("free(%x %s) from task %s\n", buf, format_memory_size_and_flags(((struct memcheck_hdr *)ptr)->length, flags), current_task->name);
+ exm_printf("free(%x %s) from task %s\n", buf, format_memory_size_and_flags(((struct memcheck_hdr *)ptr)->length, flags), current_task->name);
You could also enable all the debug messages from there (define MEM_DEBUG in mem.c), but they are very verbose; the only way to see them is to save them to card (also enable CONSOLE_DEBUG in console.c).
2) this pointed to a 31MB block allocated from raw_diag.c at line 566 (where it allocates something named "second_buf" to copy one of the images there)
3) annotated raw_diag.c to see when this part of the code was changed, and why (hg blame modules/raw_diag/raw_diag.c | grep second_buf)
Result: this part of the code is the one from 2014. If anything, the memory backend is handling this large malloc request differently (in the past it used shoot_malloc, now it prefers SRM), so we need to either provide some hints to the memory allocator, or to tweak the heuristics in mem.c. This is not exactly well explained in comments, but the preference for shoot_malloc vs srm_malloc is controlled by MEM_TEMPORARY and MEM_SRM (which should be renamed to something more consistent, as the SRM buffer is something that has to be freed right away, but you can get away with using the "shoot" buffer for a while). The first "temporary" flag was created when we only knew about the existence of the "shoot" buffer (which shouldn't be kept allocated during the entire ML session, otherwise ERR70 with certain operations, so... that memory should be only "temporarily" allocated, i.e. freed when we are done with it), and then we discovered SRM buffers, which have even more usage restrictions (you can't keep any of these allocated if you want to take a picture with Canon routines, and you *have* to free them in exactly the order they were allocated).
So, using tmp_malloc in raw_diag.c for that large buffer, fixes the issue, but these wrappers should really be renamed to something that makes sense. Maybe @names_are_hard has a better idea here

Edit: new build on the experiments page (commit
f7947b6).
Now, the question is whether to trust these numbers; the biggest issues are, in my opinion:
1) white level should be manually checked for correctness
2) sensor response curve should be evaluated (there are tweaks that affect the response curve, from what I could tell, and the current code assumes perfect linearity)
A lower-hanging fruit might be automating the whole procedure (e.g. with Lua scripts) and checking its repeatability against various test scenes, settings etc, but definitely time-consuming. In theory, this should give correct results on any out-of-focus scene covering all tone ranges from deep blacks to overexposure (detailed requirements
here).
I'm tempted to build a dark box with software-controllable lighting, similar to the one at Apertus, but I doubt it will happen during this holiday.