.close = ll_vm_close,
};
-int ll_mmap_check_compression(struct file *file, loff_t off)
-{
- struct inode *inode = file_inode(file);
- struct ll_inode_info *lli = ll_i2info(inode);
- struct lu_env *env;
- struct cl_io *io;
- __u16 refcheck;
- int rc = 0;
- __u32 gen;
-
- /* mmap reads of compressed files are supported */
- if ((file->f_flags & O_ACCMODE) == O_RDONLY)
- RETURN(0);
-
- rc = ll_layout_refresh(inode, &gen);
- if (rc) {
- CERROR("%s: "DFID" cannot refresh layout: rc = %d\n",
- ll_i2sbi(inode)->ll_fsname, PFID(&lli->lli_fid), rc);
- RETURN(rc);
- }
- if (ll_layout_version_get(lli) == CL_LAYOUT_GEN_EMPTY)
- RETURN(0);
- /* XXX: ignore PCC for a while */
- if (lli->lli_pcc_inode || lli->lli_pcc_generation)
- RETURN(0);
-
- rc = cl_io_get(inode, &env, &io, &refcheck);
- if (rc <= 0)
- RETURN(rc);
-
- rc = cl_io_init(env, io, CIT_MISC, lli->lli_clob);
- if (rc == 0) {
- if (io->ci_compressed_file)
- rc = -EOPNOTSUPP;
- } else if (rc == 1 || rc == -ENODATA) {
- rc = 0;
- } else {
- CERROR("%s: "DFID" cl_io_init() failed: rc = %d\n",
- ll_i2sbi(inode)->ll_fsname, PFID(&lli->lli_fid), rc);
- }
-
- cl_io_fini(env, io);
-
- cl_env_put(env, &refcheck);
-
- RETURN(rc);
-}
-
int ll_file_mmap(struct file *file, struct vm_area_struct * vma)
{
struct inode *inode = file_inode(file);
if (ll_file_nolock(file))
RETURN(-EOPNOTSUPP);
- rc = ll_mmap_check_compression(file, (loff_t)vma->vm_pgoff<<PAGE_SHIFT);
- if (rc)
- RETURN(rc);
-
rc = pcc_file_mmap(file, vma, &cached);
if (cached && rc != 0)
RETURN(rc);
--- /dev/null
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * GPL HEADER END
+ */
+
+/*
+ * Copyright 2023 DDN Inc. All rights reserved.
+ * Authors: Patrick Farrell <pfarrell@whamcloud.com>
+ *
+ * Simple program to copy a file using mmap. Created to test client side data
+ * compression feature.
+ *
+ * Syncs data after each write in order to force compression to act on partial
+ * chunks of data.
+ */
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/* < minimum chunk size of 64K and not-chunk or page aligned */
+#define DEFAULT_BLOCK_SIZE (35 * 1024) /* 35K */
+
+int main(int argc, char *argv[]) {
+ struct stat src_stat;
+ unsigned int random_seed = (unsigned int)time(NULL);
+ size_t block_size = DEFAULT_BLOCK_SIZE;
+ const char *src_filename;
+ const char *dst_filename;
+ size_t bytes_copied = 0;
+ float percentage = 1.0;
+ size_t remaining_size;
+ size_t chunks_to_copy;
+ int partial_copy = 0;
+ size_t copy_size;
+ void *dst_map;
+ size_t offset;
+ void *src_map;
+ int src_fd;
+ int dst_fd;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "p:s:b:")) != -1) {
+ switch (opt) {
+ case 'p':
+ partial_copy = 1;
+ percentage = atof(optarg) / 100.0;
+ if (percentage <= 0.01 || percentage > 1.0) {
+ fprintf(stderr,
+ "Percentage must be between 1 and 100\n");
+ return 1;
+ }
+ break;
+ case 's':
+ random_seed = (unsigned int)atoi(optarg);
+ break;
+ case 'b':
+ block_size = atoi(optarg);
+ if (block_size <= 0) {
+ fprintf(stderr, "Block size must be greater than 0\n");
+ return 1;
+ }
+ break;
+ default:
+ fprintf(stderr, "Usage: %s [-p PERCENT_TO_COPY(1-100)] [-s SEED] [-b BLOCK_SIZE] <source_file> <destination_file>\n",
+ argv[0]);
+ return 1;
+ }
+ }
+
+ if (optind + 2 > argc) {
+ fprintf(stderr,
+ "Usage: %s [-p PERCENT_TO_COPY(1-100)] [-s SEED] <source_file> <destination_file>\n",
+ argv[0]);
+ return 1;
+ }
+
+ src_filename = argv[optind];
+ dst_filename = argv[optind + 1];
+
+ src_fd = open(src_filename, O_RDONLY);
+ if (src_fd == -1) {
+ perror("Failed to open source file");
+ return 1;
+ }
+
+ if (fstat(src_fd, &src_stat) == -1) {
+ perror("Failed to get source file size");
+ close(src_fd);
+ return 1;
+ }
+
+ dst_fd = open(dst_filename, O_RDWR | O_CREAT, 0666);
+ if (dst_fd == -1) {
+ perror("Failed to create destination file");
+ close(src_fd);
+ return 1;
+ }
+
+ if (ftruncate(dst_fd, src_stat.st_size) == -1) {
+ perror("Failed to set destination file size");
+ close(src_fd);
+ close(dst_fd);
+ return 1;
+ }
+
+ src_map = mmap(NULL, src_stat.st_size, PROT_READ, MAP_PRIVATE, src_fd, 0);
+ if (src_map == MAP_FAILED) {
+ perror("Failed to mmap source file");
+ close(src_fd);
+ close(dst_fd);
+ return 1;
+ }
+
+ dst_map = mmap(NULL, src_stat.st_size, PROT_WRITE, MAP_SHARED, dst_fd, 0);
+ if (dst_map == MAP_FAILED) {
+ perror("Failed to mmap destination file");
+ munmap(src_map, src_stat.st_size);
+ close(src_fd);
+ close(dst_fd);
+ return 1;
+ }
+
+ remaining_size = src_stat.st_size;
+ offset = 0;
+
+ if (partial_copy) {
+ size_t total_chunks =
+ src_stat.st_size / block_size + (src_stat.st_size % block_size != 0);
+ size_t i;
+
+ chunks_to_copy = total_chunks * percentage;
+
+ srand(random_seed);
+ for (i = 0; i < chunks_to_copy; i++) {
+ offset = (rand() % (total_chunks - 1)) * block_size;
+ copy_size = (src_stat.st_size - offset < block_size) ?
+ src_stat.st_size - offset : block_size;
+ memcpy(dst_map + offset, src_map + offset, copy_size);
+ bytes_copied += copy_size;
+ fsync(dst_fd);
+ }
+ } else {
+ /* Existing copy logic */
+ while (remaining_size > 0) {
+ copy_size = (remaining_size < block_size) ?
+ remaining_size : block_size;
+ memcpy(dst_map + offset, src_map + offset, copy_size);
+ offset += copy_size;
+ remaining_size -= copy_size;
+ bytes_copied += copy_size;
+ fsync(dst_fd);
+ }
+ }
+ munmap(src_map, src_stat.st_size);
+ munmap(dst_map, src_stat.st_size);
+ close(src_fd);
+ close(dst_fd);
+
+ if (!partial_copy)
+ printf("File '%s' (%lu bytes) mmap-copied to '%s' successfully (block size %lu)!\n",
+ src_filename, bytes_copied, dst_filename, block_size);
+ else
+ printf("Copied %lu bytes (%d%%) of file '%s' (%lu total bytes) mmap-copied to '%s' successfully (block size %lu)!\n",
+ bytes_copied, (int)(percentage * 100), src_filename,
+ src_stat.st_size, dst_filename, block_size);
+
+ return 0;
+}
+
}
run_test 1002 "test reads with incomplete chunks"
+test_1003() {
+ (( MDS1_VERSION >= $(version_code 2.14.0.91) )) ||
+ skip "Need MDS version at least 2.14.0.91"
+
+ local tf=$DIR/$tfile
+ local hdf=$LUSTRE/tests/AMSR_E_L3_DailyOcean_V05_20111003.hdf
+ local tmp_hdf=$TMP/$tfile.hdf
+ local source=$tmp_hdf
+ # Larger than arm page size
+ local chunksize=128
+
+ if [[ -f $hdf.bz2 ]] && type -p bzcat >/dev/null; then
+ bzcat $hdf.bz2 > $tmp_hdf
+ elif [[ -f $hdf.bz2 ]] && type -p bunzip2 >/dev/null; then
+ cp $hdf.bz2 $tmp_hdf.bz2 || error "cp $tmp_hdf.bz2"
+ bunzip2 $tmp_hdf.bz2 || error "bunzip2 $tmp_hdf.bz2"
+ else
+ echo "bunzip2 is not installed, skip it"
+ return 0
+ fi
+
+ # Fail test if source size changes so we catch this
+ # Source should be a few MiB in size
+ $CHECKSTAT -s 14625450 $source || error "checkstat wrong size"
+
+ stack_trap "rm -f $tf; disable_compression"
+ enable_compression
+
+ local chunksizes=(
+ 64
+ 128
+ 1024
+ )
+ local blocksizes=(
+ # Use a mix of chunk aligned and chunk unaligned sizes
+ $((35 * 1024)) # 35K (etc)
+ $((64 * 1024))
+ $((100 * 1024))
+ $((128 * 1024))
+ )
+
+ local seed=$(date +%s)
+ echo "Random seed $seed."
+ # Just test mmap reads, read-modify-write is not supported yet
+ for chunksize in "${chunksizes[@]}"; do
+ for blocksize in "${blocksizes[@]}"; do
+ rm -f $tf*
+ echo "Compression, using ${chunksize}K chunksize"
+ $LFS setstripe -E -1 -Z lz4:0 --compress-chunk="$chunksize" "$tf" ||
+ error "setstripe on $tf failed, chunsksize $chunksize"
+
+ dd if=$source bs=1M of=$tf oflag=direct ||
+ error "copy to create $tf failed"
+ flush_and_compare $source $tf "(0)"
+
+ # Copy from compressed file to non-compressed file
+ # (test reading)
+ mmap_copy_file -b $blocksize $tf $tf.2 ||
+ error "mmap file copy failed (1)"
+
+ flush_and_compare $source $tf.2 "(2)"
+
+ rm -f $tf.2
+ rm -f $tf.3
+
+ # Test reading again by copying random chunks totalling 50% of
+ # file size (but copies are at random offsets)
+ # We seed the random copy and copy from the original source,
+ # then repeat the random copy from the compressed file with the
+ # same seed and compare those two
+ mmap_copy_file -s $seed -p 50 -b $blocksize $source $tf.2 ||
+ error "mmap file copy failed (3)"
+ mmap_copy_file -s $seed -p 50 -b $blocksize $tf $tf.3 ||
+ error "mmap file copy failed (4)"
+
+ flush_and_compare $tf.2 $tf.3 "(5)"
+ done
+ done
+}
+run_test 1003 "mmap test for compression"
+
complete_test $SECONDS
check_and_cleanup_lustre
declare -a logs=($ONLY)