btrfs의 FIDEDUPERANGE ioctl이 예상대로 작동하지 않습니다.

btrfs의 FIDEDUPERANGE ioctl이 예상대로 작동하지 않습니다.

~에 따르면ioctl_fideduperange,

최대 크기는src_length파일 시스템에 따라 다르지만 일반적으로 16MiB입니다.

src_length그러나 단일 호출로 1Gib 이상을 성공적으로 사용할 수 있었습니다 ioctl. 적어도 16MiB에 대한 경고는 완전히 과장된 것입니까 btrfs?

게다가 에 따르면VFS 문서,

구현에서는 len == 0으로 전달되는 호출자를 처리해야 합니다. 이는 "소스 파일의 끝으로 다시 매핑"을 의미합니다.

그러나 으로 설정하려고 하면 호출은 src_length성공 하지만 아무 작업도 수행되지 않습니다.0ioctl

제가 이 두 문장을 잘못 읽었나요? 아니면 btrfs구현이 단순히 (아주 좋은) 문서를 따르지 않는 걸까요? 커널을 사용하여 Linux Mint 20에서 테스트하고 있습니다 5.4.0-62-generic. 나는 이를 사용하여 filefrag -sv FILE1 FILE2파일의 블록 수준 할당을 확인하여 파일이 중복되었는지 확인합니다. 중복 파일을 제거하려면 다음 프로그램을 사용하고 있습니다. 문제의 파일 btrfssudo mkfs.btrfs -mraid1 -draid1 /dev/mapper/sda1_crypt /dev/mapper/sdb1_crypt.

상상하다:

$ cp -f file1 file2
$ filefrag -sv file1 file2   # see that files use different extents (are not deduplicated)
$ myprog file1 file2
$ filefrag -sv file1 file2   # see that files use the same extents (have been deduplicated)

두 파일에서 중복 항목을 제거하는 절차:

// deduplicate srcfile and targetfile if contents are identical
// usage:  myprog srcfile targetfile
// compile with:  gcc myprog.c -o myprog

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>

int main(int argc, char**argv)
{
   struct stat st;
   long size;
   __u64 buf[2048];        /* __u64 for proper field alignment */
   struct file_dedupe_range *range = (struct file_dedupe_range *)buf;

   memset(range, 0, sizeof(struct file_dedupe_range));
   memset(&range->info, 0, sizeof(struct file_dedupe_range_info));

   long srcfd = open(argv[1], O_RDONLY);
   if (srcfd < 0) { perror("open-src"); exit(1); }
   if (fstat(srcfd, &st) < 0) { perror("stat-src"); exit(1); }
   size = st.st_size;

   long tgtfd = open(argv[2], O_RDWR);
   if (tgtfd < 0) { perror("open-tgt"); exit(1); }
   if (fstat(tgtfd, &st) < 0) { perror("stat-tgt"); exit(1); }
   if (size != st.st_size) {
      fprintf(stderr, "SIZE DIFF\n");
      exit(1);
   }

   range->src_offset = 0;
   range->src_length = size;
// range->src_length = 0;                    // I expected this to work
   range->dest_count = 1;
   range->info[0].dest_fd = tgtfd;
   range->info[0].dest_offset = 0;

   while (range->src_length > 0) {
      if (ioctl(srcfd, FIDEDUPERANGE, range) < 0) { perror("ioctl"); exit(1); }

      fprintf(stderr, "bytes_deduped: %llu\n", range->info[0].bytes_deduped);
      fprintf(stderr, "status: %d\n", range->info[0].status);
      if (range->info[0].status == FILE_DEDUPE_RANGE_DIFFERS) {
         fprintf(stderr, "DIFFERS\n");
         break;
      } else if (range->info[0].status == FILE_DEDUPE_RANGE_SAME) {
         fprintf(stderr, "SAME\n");
      } else {
         fprintf(stderr, "ERROR\n");
         break;
      }

      if (range->info[0].bytes_deduped >= range->src_length) { break; }
      range->src_length -= range->info[0].bytes_deduped;
      range->src_offset += range->info[0].bytes_deduped;
      range->info[0].dest_offset += range->info[0].bytes_deduped;
   }
   exit(0);
}

답변1

이전 버전 btrfs(예: 4.15) FIDEDUPERANGE은 호출당 16MiB로 제한되며 매우 큰 요청을 자동으로 16MiB로 줄입니다. 언제 변경이 발생했는지 정확히 잊어버렸지만 현재 버전 btrfs(예: 5.16)은 16MiB 청크 단위로 순환합니다. 그러나 내 생각에는 Linux( btrfs지금은 아님)가 여전히 요청을 1GiB 이상 자동으로 줄이는 것 같습니다. FIDEDUPERANGE이전 버전을 사용하려면 btrfs16MiB 제한을 반드시 준수해야 합니다. 또한 다른 파일 시스템에도 비슷한 제한이 있을 수 있습니다.

그에 관해서는 사용 방법에 대한 지침을 보려면 src_length = 0각 개인의 문서를 참조해야 합니다 . ioctl올바르게 참조하고 있는 페이지는 man아무것도 제거되지 않음을 의미합니다.FIDEDUPERANGEsrc_length = 0

당신이 참조한 VFS 페이지에 관해서는 상황이 복잡합니다. 원래 개별적으로 설계 및 구현된 여러 항목에서 적용된 remap_file_range()기능을 처리합니다. clone s에서는 파일 끝까지 복제하는 것을 의미합니다. 중복 제거에서는 중복 제거가 수행되지 않음을 의미합니다. 정확히 언제인지는 기억나지 않지만 복제와 중복 제거 기능을 통합하려는 노력이 있었습니다. 그러나 버전 5.16에서는 복제 호출인지 중복 제거 호출인지에 따라 매개변수를 변환하는 이상한 해킹이 있습니다. 여기서는 동작이 변경되지 않았다고 생각하기 때문에 VFS 문서가 잘못되었다고 말하기 쉽습니다. 하지만 다른 파일 시스템에서 이 의미를 구현했는지는 확실하지 않아 복잡합니다.ioctlbtrfsbtrfsioctlsrc_length == 0ioctlsrc_length == 0ioctlbtrfsbtrfs_remap_file_range_prep()lenremap_file_range()ioctlbtrfsremap_file_range()len == 0

관련 정보