btrfs の FIDEDUPERANGE ioctl が期待どおりに動作しません。

btrfs の FIDEDUPERANGE ioctl が期待どおりに動作しません。

~によるとioctl_fideduperange

最大サイズはsrc_lengthファイルシステムによって異なりますが、通常16MiBです。

src_lengthただし、単一の呼び出しで1Gib以上を正常に使用できましたioctl。少なくとも16MiBの警告は完全に誇張されていますかbtrfs

その上によるとVFSドキュメント

実装では、len == 0で渡される呼び出し元を処理する必要があります。これは、「ソースファイルの末尾への再マッピング」を意味します。

ただし、に設定しようとすると呼び出しは成功src_lengthしますが、何もしません。0ioctl

私はこの2つの文を誤って読んでいますか?それとも、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

おすすめ記事