Linux-Mint

FIDEDUPERANGE ioctl 在 btrfs 上的行為不符合預期

  • January 23, 2022

根據ioctl_fideduperange

的最大大小*src_length*取決於文件​​系統,通常為 16 MiB。

但是,我已經能夠通過src_length一次呼叫成功使用 > 1 Gib ioctl。至少對於 16 MiB 的警告是否完全誇大了btrfs

此外,根據VFS 文件

實現必須處理傳入 len == 0; 的呼叫者;這意味著“重新映射到源文件的末尾”。

但是,當我嘗試設置src_length0時,ioctl呼叫成功但不執行任何操作。

我是誤讀了這兩句話,還是btrfs實現根本不符合(很好)文件?我正在使用 kernel 在 Linux Mint 20 上進行測試5.4.0-62-generic。我filefrag -sv FILE1 FILE2用來檢查文件的塊級分配,看看它們是否重複。我正在使用下面的程序對文件進行重複數據刪除。有問題的文件位於btrfs使用sudo 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);
}

舊版本btrfs(例如,4.15)每次FIDEDUPERANGE呼叫有 16 MiB 的限制,並且會默默地將超大請求減少到 16 MiB。我忘記了更改發生的確切時間,但目前版本btrfs(即 5.16)以 16 MiB 塊循環。不過,我認為 linux(btrfs現在不是)仍然默默地減少超過 1 GiB 的請求。如果您希望FIDEDUPERANGE與舊版本一起使用btrfs,您絕對應該遵守 16 MiB 的限制。此外,其他文件系統可能有類似的限制。

至於src_length = 0,您應該真正查閱個人ioctls 的文件以獲取有關如何使用它們的說明。您正確引用的man頁面FIDEDUPERANGE文件src_length = 0意味著沒有重複數據刪除。

關於您引用的 VFS 頁面,事情很複雜。 remap_file_range()處理ioctl改編自btrfs最初在 中單獨設計和實現的多個 s 的功能btrfs。在 cloneioctl中,src_length == 0表示複製到文件末尾。在 dedupioctl中,src_length == 0意味著 dedup 什麼都沒有。我忘記了確切的時間,但有人努力統一複製和重複數據刪除功能。但是,更改ioctl目前支持的btrfs. 在 5.16 版中,有一個奇怪的 hack 涉及btrfs_remap_file_range_prep()len參數轉換為remap_file_range()取決於ioctl是複製或重複數據刪除呼叫。很容易說 VFS 文件是錯誤的,因為我認為btrfs這裡的行為沒有改變。但是,我不確定其他文件系統是否已經實現remap_file_range()了這個含義len == 0,所以它很複雜。

引用自:https://unix.stackexchange.com/questions/629759