package:busybox
version:1.34.1

kernel:linux 5.10






When using tar to decompress an xz-compressed file stored in memory to nor 
flash, the execution time occasionally doubles.(6 out of 50 times, with 
kernel-mode execution time nearly doubling from 13.32s to 20.80)
time tar Jxf /tmp/a.tar.xz -C /nor_flash
Normal case:
     real     0m 53.14s
     user    0m 0.75s
     sys      0m 13.32s
Abnormal case:
     real     1m 2.79s
     user    0m 0.77s
     sys      0m 20.80s






Through debugging, it was found that the issue occurs when the count parameter 
passed to the first write() system call for decompressing a single file is not 
4096 bytes.


Normal case:
     write(fd, buf, count=4096) --  first
     …  --  All intermediate writes with count parameter value 4096
     write(fd, buf, count=4096)


Abnormal case:
     write(fd, buf, count=2414) --  first
     …  --  All intermediate writes with count parameter value 4096
     write(fd, buf, count=1682)


Function call path:
    archival/tar.c : get_header_tar
        archival/libarchive/data_extract_all.c : data_extract_all
            libbb/copyfd.c : bb_copyfd_size
                libbb/copyfd.c : bb_full_fd_action
                    libbb/full_write.c : full_write
                        libbb/safe_write.c : safe_write
                            write


Further analysis shows that the count parameter in the write() system call 
originates from the return value of safe_read() in the bb_full_fd_action() 
function. In abnormal case, the first safe_read() does not return 4096 because 
there was not enough data ready in the input fd(the output fd of 
unpack_xz_stream() function).




Fault injection attempts showed that by modifying bb_full_fd_action() to force 
the first safe_read() to read only 2414 bytes, the issue could be reproduced 
consistently.


@@ -84,8 +84,15 @@ static off_t bb_full_fd_action(int src_f
            }
        }
#endif
-       rd = safe_read(src_fd, buffer,
-           size > buffer_size ? buffer_size : size);
+       static bool first = true;
+       if(first) {
+           rd = safe_read(src_fd, buffer,
+               size > 2414 ? 2414 : size);
+           first = false;
+       } else {
+           rd = safe_read(src_fd, buffer,
+               size > buffer_size ? buffer_size : size);
+       }
         if (rd < 0) {
             bb_simple_perror_msg(bb_msg_read_error);
             break;




As a workaround, replacing safe_read() with full_read() in the 
bb_full_fd_action() function eliminated the issue (no performance degradation 
observed over 200 runs).
@@ -84,7 +84,7 @@ static off_t bb_full_fd_action(int src_f
            }
        }
#endif
-      rd = safe_read(src_fd, buffer,
+      rd = full_read(src_fd, buffer,
              size > buffer_size ? buffer_size : size);
         if (rd < 0) {
             bb_simple_perror_msg(bb_msg_read_error);






Questions:
    The bb_full_fd_action() function is used in many places. Is it feasible to 
replace safe_read() with full_read()? What impacts need to be evaluated?
    If replacement is not feasible, are there any recommended alternative 
approaches to ensure that each write() system call during tar decompression 
passes a count parameter value of 4096 when writing to flash?
_______________________________________________
busybox mailing list
[email protected]
https://lists.busybox.net/mailman/listinfo/busybox

Reply via email to