From 77d65169ac57fffebfd30b50f4bfa6fa9cc1c8af Mon Sep 17 00:00:00 2001 From: Abhishek Gupta Date: Thu, 16 Jul 2026 15:29:34 +0000 Subject: [PATCH 1/5] Using already defined constant for MiB --- cmd/mount.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cmd/mount.go b/cmd/mount.go index e95d66f977..020b68672a 100644 --- a/cmd/mount.go +++ b/cmd/mount.go @@ -32,16 +32,12 @@ import ( "github.com/googlecloudplatform/gcsfuse/v3/internal/gcsx" "github.com/googlecloudplatform/gcsfuse/v3/internal/logger" "github.com/googlecloudplatform/gcsfuse/v3/internal/perms" + "github.com/googlecloudplatform/gcsfuse/v3/internal/util" "github.com/jacobsa/fuse" "github.com/jacobsa/fuse/fsutil" "github.com/jacobsa/timeutil" ) -const ( - KiB = 1024 - MiB = 1024 * KiB -) - // Mount the file system based on the supplied arguments, returning a // fuse.MountedFileSystem that can be joined to wait for unmounting. func mountWithStorageHandle( @@ -208,7 +204,7 @@ func getFuseMountConfig(fsName string, newConfig *cfg.Config) *fuse.MountConfig if newConfig.FileSystem.FuseMaxRequestSizeKb > 0 { mountCfg.MaxPages = uint16(cfg.MaxPagesForRequestSizeKb(int(newConfig.FileSystem.FuseMaxRequestSizeKb))) - mountCfg.MaxWrite = uint32(1 * MiB) + mountCfg.MaxWrite = uint32(1 * util.MiB) } if newConfig.Logging.WireLog != "" { From 106ad2e2686702e952fabe91a0f458c48177cc59 Mon Sep 17 00:00:00 2001 From: Abhishek Gupta Date: Thu, 16 Jul 2026 18:30:42 +0000 Subject: [PATCH 2/5] Integration tests to verify max_pages & max_write behavior --- .../max_request_size_read_test.go | 111 ++++++++++++++++++ tools/integration_tests/test_config.yaml | 18 +++ .../max_request_size_test.go | 80 +++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 tools/integration_tests/flag_optimizations/max_request_size_read_test.go create mode 100644 tools/integration_tests/write_large_files/max_request_size_test.go diff --git a/tools/integration_tests/flag_optimizations/max_request_size_read_test.go b/tools/integration_tests/flag_optimizations/max_request_size_read_test.go new file mode 100644 index 0000000000..6394495c28 --- /dev/null +++ b/tools/integration_tests/flag_optimizations/max_request_size_read_test.go @@ -0,0 +1,111 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package flag_optimizations + +import ( + "fmt" + "log" + "os" + "path" + "strconv" + "strings" + "testing" + + "github.com/googlecloudplatform/gcsfuse/v3/tools/integration_tests/util/operations" + "github.com/googlecloudplatform/gcsfuse/v3/tools/integration_tests/util/setup" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "golang.org/x/sys/unix" +) + +type MaxRequestSizeReadSuite struct { + suite.Suite + flags []string +} + +func (s *MaxRequestSizeReadSuite) SetupSuite() { + mustMountGCSFuseAndSetupTestDir(s.flags, testEnv.ctx, testEnv.storageClient) +} + +func (s *MaxRequestSizeReadSuite) TearDownSuite() { + tearDownOptimizationTest(s.T()) +} + +func (s *MaxRequestSizeReadSuite) TestKernelReaderLargeRead16MiB() { + // 1. Check if the host kernel / FUSE connection supports max_pages >= 4096 (16 MiB / 4 KiB per page = 4096 pages). + if data, err := os.ReadFile("/proc/sys/fs/fuse/max_pages_limit"); err == nil { + if limit, err := strconv.Atoi(strings.TrimSpace(string(data))); err == nil && limit < 4096 { + s.T().Skipf("Skipping TestKernelReaderLargeRead16MiB: /proc/sys/fs/fuse/max_pages_limit (%d) is less than 4096 pages (16 MiB)", limit) + } + } + var stat unix.Stat_t + if err := unix.Stat(setup.MntDir(), &stat); err == nil { + devMinor := unix.Minor(stat.Dev) + maxPagesPath := fmt.Sprintf("/sys/fs/fuse/connections/%d/max_pages", devMinor) + if data, err := os.ReadFile(maxPagesPath); err == nil { + if limit, err := strconv.Atoi(strings.TrimSpace(string(data))); err == nil && limit < 4096 { + s.T().Skipf("Skipping TestKernelReaderLargeRead16MiB: connection max_pages (%d) is less than 4096 pages (16 MiB)", limit) + } + } + } + + // 2. Create 16 MiB test file in GCS. + testName := strings.ReplaceAll(s.T().Name(), "/", "_") + fileName := path.Join(testEnv.testDirPath, testName+"_16MiB.txt") + operations.CreateFileOfSize(16*1024*1024, fileName, s.T()) + s.T().Cleanup(func() { + _ = os.Remove(fileName) + }) + + // 3. Truncate log file to record only the read operation. + err := os.Truncate(setup.LogFile(), 0) + require.NoError(s.T(), err, "Failed to truncate log file") + + // 4. Perform a 16 MiB read. + content, err := os.ReadFile(fileName) + require.NoError(s.T(), err, "Failed to read 16 MiB file") + require.Len(s.T(), content, 16*1024*1024) + + // 5. Verify in gcsfuse trace log that exactly 1 read request of 16 MiB (16777216 bytes) occurred. + logContent, err := os.ReadFile(setup.LogFile()) + require.NoError(s.T(), err, "Failed to read log file after read") + + lines := strings.Split(string(logContent), "\n") + var readRequestCount int + for _, line := range lines { + if strings.Contains(line, "<- ReadFile") { + readRequestCount++ + assert.Contains(s.T(), line, "16777216", "Expected read request to be 16 MiB (16777216 bytes), but got line: %s", line) + } + } + assert.Equal(s.T(), 1, readRequestCount, "Expected exactly 1 read request of 16 MiB when kernel reader and fuse max pages support are enabled, got %d", readRequestCount) +} + +func TestKernelReaderLargeReadSuite(t *testing.T) { + if setup.IsDynamicMount(testEnv.mountDir, testEnv.rootDir) { + t.Skip("Skipping test for dynamic mounting") + } + flagsSet := setup.BuildFlagSets(testEnv.cfg, testEnv.bucketType, t.Name()) + for _, flags := range flagsSet { + t.Run(strings.Join(flags, "_"), func(t *testing.T) { + log.Printf("Running tests with flags: %s", flags) + s := &MaxRequestSizeReadSuite{ + flags: flags, + } + suite.Run(t, s) + }) + } +} diff --git a/tools/integration_tests/test_config.yaml b/tools/integration_tests/test_config.yaml index 4752bda8ae..d1e20a1fb3 100644 --- a/tools/integration_tests/test_config.yaml +++ b/tools/integration_tests/test_config.yaml @@ -150,6 +150,15 @@ write_large_files: - mounted_directory: "${MOUNTED_DIR}" test_bucket: "${BUCKET_NAME}" configs: + - run: TestWriteMaxRequestSize16MiB + flags: + - "--enable-kernel-reader=true,--fuse-max-request-size-kb=16384,--log-severity=trace,--client-protocol=http1" + - "--enable-kernel-reader=true,--fuse-max-request-size-kb=16384,--log-severity=trace,--client-protocol=grpc" + compatible: + flat: true + hns: true + zonal: false + run_on_gke: true - flags: - "--enable-streaming-writes=false,--client-protocol=http1" - "--enable-streaming-writes=false,--client-protocol=grpc" @@ -1355,6 +1364,15 @@ flag_optimizations: - mounted_directory: "${MOUNTED_DIR}" test_bucket: "${BUCKET_NAME}" configs: + - run: TestKernelReaderLargeReadSuite + flags: + - "--enable-kernel-reader=true,--fuse-max-request-size-kb=16384,--log-severity=trace,--client-protocol=http1" + - "--enable-kernel-reader=true,--fuse-max-request-size-kb=16384,--log-severity=trace,--client-protocol=grpc" + compatible: + flat: true + hns: true + zonal: false + run_on_gke: true - run: TestMountFails flags: - "--profile=unknown-profile,--client-protocol=http1" diff --git a/tools/integration_tests/write_large_files/max_request_size_test.go b/tools/integration_tests/write_large_files/max_request_size_test.go new file mode 100644 index 0000000000..600b31aacb --- /dev/null +++ b/tools/integration_tests/write_large_files/max_request_size_test.go @@ -0,0 +1,80 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package write_large_files + +import ( + "os" + "path" + "strings" + "syscall" + "testing" + + "github.com/googlecloudplatform/gcsfuse/v3/tools/integration_tests/util/operations" + "github.com/googlecloudplatform/gcsfuse/v3/tools/integration_tests/util/setup" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + writeFileStartMsg = "<- WriteFile" + sixteenMiB = 16 * 1024 * 1024 + oneMiBInBytes = "1048576" +) + +func TestWriteMaxRequestSize16MiB(t *testing.T) { + logContent, err := os.ReadFile(setup.LogFile()) + require.NoError(t, err, "Failed to read log file") + if !strings.Contains(string(logContent), "--fuse-max-request-size-kb=16384") { + t.Skip("Skipping TestWriteMaxRequestSize16MiB: --fuse-max-request-size-kb=16384 is not set in the mount config") + } + + // Setup test directory and target file. + writeDir := setup.SetupTestDirectory("dirForMaxRequestSizeWrite") + filePath := path.Join(writeDir, "16MiB_write_test_"+setup.GenerateRandomString(5)+".txt") + + // Truncate the log file right before writing so only our write operations are logged. + err = os.Truncate(setup.LogFile(), 0) + require.NoError(t, err, "Failed to truncate log file") + + // Generate 16 MiB of random data. + data, err := operations.GenerateRandomData(sixteenMiB) + require.NoError(t, err, "Failed to generate random data") + + // Open file with O_DIRECT so writes go straight to FUSE without kernel page-cache buffering. + f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC|syscall.O_DIRECT, setup.FilePermission_0600) + require.NoError(t, err, "Failed to open file for direct write") + + // Write 16 MiB in a single buffer call. + n, err := f.Write(data) + require.NoError(t, err, "Failed to write 16 MiB to file") + require.Equal(t, sixteenMiB, n) + + err = f.Close() + require.NoError(t, err, "Failed to close file") + + // Read log content after write. + logContent, err = os.ReadFile(setup.LogFile()) + require.NoError(t, err, "Failed to read log file after write") + + lines := strings.Split(string(logContent), "\n") + var writeRequestCount int + for _, line := range lines { + if strings.Contains(line, writeFileStartMsg) { + writeRequestCount++ + assert.Contains(t, line, oneMiBInBytes, "Expected write request to be 1 MiB (%s bytes), but got line: %s", oneMiBInBytes, line) + } + } + assert.Equal(t, 16, writeRequestCount, "Expected exactly 16 write requests of 1 MiB each for 16 MiB write, got %d", writeRequestCount) +} From 5617454bfa1ead7e9fc10241526021ba9b84c5d2 Mon Sep 17 00:00:00 2001 From: Abhishek Gupta Date: Fri, 17 Jul 2026 06:01:29 +0000 Subject: [PATCH 3/5] Minor improvements --- .../max_request_size_read_test.go | 38 +++++++++---------- .../util/operations/file_operations.go | 7 +++- .../util/operations/operations.go | 12 +++++- .../max_request_size_test.go | 2 +- 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/tools/integration_tests/flag_optimizations/max_request_size_read_test.go b/tools/integration_tests/flag_optimizations/max_request_size_read_test.go index 6394495c28..7e94a9a0cd 100644 --- a/tools/integration_tests/flag_optimizations/max_request_size_read_test.go +++ b/tools/integration_tests/flag_optimizations/max_request_size_read_test.go @@ -15,7 +15,6 @@ package flag_optimizations import ( - "fmt" "log" "os" "path" @@ -28,7 +27,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "golang.org/x/sys/unix" +) + +const ( + sixteenMiB = 16 * operations.MiB + sixteenMiBInBytes = "16777216" ) type MaxRequestSizeReadSuite struct { @@ -45,39 +48,32 @@ func (s *MaxRequestSizeReadSuite) TearDownSuite() { } func (s *MaxRequestSizeReadSuite) TestKernelReaderLargeRead16MiB() { - // 1. Check if the host kernel / FUSE connection supports max_pages >= 4096 (16 MiB / 4 KiB per page = 4096 pages). - if data, err := os.ReadFile("/proc/sys/fs/fuse/max_pages_limit"); err == nil { - if limit, err := strconv.Atoi(strings.TrimSpace(string(data))); err == nil && limit < 4096 { - s.T().Skipf("Skipping TestKernelReaderLargeRead16MiB: /proc/sys/fs/fuse/max_pages_limit (%d) is less than 4096 pages (16 MiB)", limit) - } + // 1. Check if the host kernel supports elevating max_pages limit to what is needed for a 16 MiB read. + requiredPages := sixteenMiB / os.Getpagesize() + data, err := os.ReadFile("/proc/sys/fs/fuse/max_pages_limit") + if err != nil { + s.T().Skipf("Skipping TestKernelReaderLargeRead16MiB: kernel does not support elevating max_pages limit (/proc/sys/fs/fuse/max_pages_limit: %v)", err) } - var stat unix.Stat_t - if err := unix.Stat(setup.MntDir(), &stat); err == nil { - devMinor := unix.Minor(stat.Dev) - maxPagesPath := fmt.Sprintf("/sys/fs/fuse/connections/%d/max_pages", devMinor) - if data, err := os.ReadFile(maxPagesPath); err == nil { - if limit, err := strconv.Atoi(strings.TrimSpace(string(data))); err == nil && limit < 4096 { - s.T().Skipf("Skipping TestKernelReaderLargeRead16MiB: connection max_pages (%d) is less than 4096 pages (16 MiB)", limit) - } - } + if limit, err := strconv.Atoi(strings.TrimSpace(string(data))); err != nil || limit < requiredPages { + s.T().Skipf("Skipping TestKernelReaderLargeRead16MiB: /proc/sys/fs/fuse/max_pages_limit (%s, err: %v) is less than required %d pages for 16 MiB read (page size %d bytes)", strings.TrimSpace(string(data)), err, requiredPages, os.Getpagesize()) } // 2. Create 16 MiB test file in GCS. testName := strings.ReplaceAll(s.T().Name(), "/", "_") fileName := path.Join(testEnv.testDirPath, testName+"_16MiB.txt") - operations.CreateFileOfSize(16*1024*1024, fileName, s.T()) + operations.CreateFileOfSize(sixteenMiB, fileName, s.T()) s.T().Cleanup(func() { _ = os.Remove(fileName) }) // 3. Truncate log file to record only the read operation. - err := os.Truncate(setup.LogFile(), 0) + err = os.Truncate(setup.LogFile(), 0) require.NoError(s.T(), err, "Failed to truncate log file") // 4. Perform a 16 MiB read. content, err := os.ReadFile(fileName) require.NoError(s.T(), err, "Failed to read 16 MiB file") - require.Len(s.T(), content, 16*1024*1024) + require.Len(s.T(), content, sixteenMiB) // 5. Verify in gcsfuse trace log that exactly 1 read request of 16 MiB (16777216 bytes) occurred. logContent, err := os.ReadFile(setup.LogFile()) @@ -86,9 +82,9 @@ func (s *MaxRequestSizeReadSuite) TestKernelReaderLargeRead16MiB() { lines := strings.Split(string(logContent), "\n") var readRequestCount int for _, line := range lines { - if strings.Contains(line, "<- ReadFile") { + if strings.Contains(line, readFileStartMsg) { readRequestCount++ - assert.Contains(s.T(), line, "16777216", "Expected read request to be 16 MiB (16777216 bytes), but got line: %s", line) + assert.Contains(s.T(), line, sixteenMiBInBytes, "Expected read request to be 16 MiB (%s bytes), but got line: %s", sixteenMiBInBytes, line) } } assert.Equal(s.T(), 1, readRequestCount, "Expected exactly 1 read request of 16 MiB when kernel reader and fuse max pages support are enabled, got %d", readRequestCount) diff --git a/tools/integration_tests/util/operations/file_operations.go b/tools/integration_tests/util/operations/file_operations.go index 1eb54c10f3..a976c47f82 100644 --- a/tools/integration_tests/util/operations/file_operations.go +++ b/tools/integration_tests/util/operations/file_operations.go @@ -32,6 +32,7 @@ import ( "testing" "time" + "github.com/googlecloudplatform/gcsfuse/v3/internal/cache/util" "github.com/googlecloudplatform/gcsfuse/v3/internal/storage/gcs" "github.com/googlecloudplatform/gcsfuse/v3/tools/integration_tests/util/setup" "github.com/stretchr/testify/assert" @@ -276,7 +277,11 @@ func WriteFilesSequentially(t *testing.T, filePaths []string, fileSize int64, ch } func ReadChunkFromFile(filePath string, chunkSize int64, offset int64, flag int) (chunk []byte, err error) { - chunk = make([]byte, chunkSize) + chunk, err = util.GetMemoryAlignedBuffer(chunkSize, int64(os.Getpagesize())) + if err != nil { + log.Printf("Error in allocating memory aligned buffer: %v", err) + return + } file, err := os.OpenFile(filePath, flag, FilePermission_0600) if err != nil { diff --git a/tools/integration_tests/util/operations/operations.go b/tools/integration_tests/util/operations/operations.go index 67e191996b..f21d31e175 100644 --- a/tools/integration_tests/util/operations/operations.go +++ b/tools/integration_tests/util/operations/operations.go @@ -19,15 +19,23 @@ import ( "bytes" "fmt" "math/rand" + "os" "os/exec" "time" + + "github.com/googlecloudplatform/gcsfuse/v3/internal/cache/util" ) // GenerateRandomData generates random data that can be used to write to a file. +// The returned slice is memory-aligned to os.Getpagesize() so that it can be +// safely used for direct I/O (O_DIRECT) operations. func GenerateRandomData(sizeInBytes int64) ([]byte, error) { r := rand.New(rand.NewSource(time.Now().UnixNano())) - data := make([]byte, sizeInBytes) - _, err := r.Read(data) + data, err := util.GetMemoryAlignedBuffer(sizeInBytes, int64(os.Getpagesize())) + if err != nil { + return nil, fmt.Errorf("util.GetMemoryAlignedBuffer(%d, %d): %w", sizeInBytes, os.Getpagesize(), err) + } + _, err = r.Read(data) if err != nil { return nil, fmt.Errorf("r.Read(): %v", err) } diff --git a/tools/integration_tests/write_large_files/max_request_size_test.go b/tools/integration_tests/write_large_files/max_request_size_test.go index 600b31aacb..a2780652f2 100644 --- a/tools/integration_tests/write_large_files/max_request_size_test.go +++ b/tools/integration_tests/write_large_files/max_request_size_test.go @@ -48,7 +48,7 @@ func TestWriteMaxRequestSize16MiB(t *testing.T) { err = os.Truncate(setup.LogFile(), 0) require.NoError(t, err, "Failed to truncate log file") - // Generate 16 MiB of random data. + // Generate 16 MiB of random data (memory-aligned to os.Getpagesize() for O_DIRECT write). data, err := operations.GenerateRandomData(sixteenMiB) require.NoError(t, err, "Failed to generate random data") From ce331e8d67a32efe942ba918a8bf59b836a9e3f5 Mon Sep 17 00:00:00 2001 From: Abhishek Gupta Date: Fri, 17 Jul 2026 09:05:10 +0000 Subject: [PATCH 4/5] Making fuse-max-request-size-kb flag conditional on enable-kernel-reader --- cfg/config.go | 2 +- cfg/params.yaml | 5 +++-- cmd/mount.go | 2 +- cmd/mount_test.go | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/cfg/config.go b/cfg/config.go index c11ff7b4d3..535b5c040e 100644 --- a/cfg/config.go +++ b/cfg/config.go @@ -1196,7 +1196,7 @@ func BuildFlagSet(flagSet *pflag.FlagSet) error { flagSet.BoolP("foreground", "", false, "Stay in the foreground after mounting.") - flagSet.IntP("fuse-max-request-size-kb", "", StorageClassRapid.DefaultFuseMaxRequestSizeKb(), "Sets the target maximum request size in KiB that FUSE can process in a single request (currently used to control read requests only). This is translated to the kernel max_pages limit based on host page size. As max_pages_limit is a global, machine-level configuration across all mounts, the host's limit is only updated if the calculated pages value is greater than the current system limit. Note that the FUSE kernel max_pages limit can be set to at most 65535 (fuse_max_max_pages), so the value of this parameter must be > 0 and translate to at most 65535 pages. Additionally, on GKE, the system-wide setting is capped to 16 MiB (16384 KiB) by default by the CSI driver. If needed to be set beyond that on GKE, the user has to manually increase the value on the node before GCSFuse mounting begins.") + flagSet.IntP("fuse-max-request-size-kb", "", StorageClassRapid.DefaultFuseMaxRequestSizeKb(), "Sets the target maximum request size in KiB that FUSE can process in a single request (currently used to control read requests only). This parameter only takes effect when enable-kernel-reader is set to true. This is translated to the kernel max_pages limit based on host page size. As max_pages_limit is a global, machine-level configuration across all mounts, the host's limit is only updated if the calculated pages value is greater than the current system limit. Note that the FUSE kernel max_pages limit can be set to at most 65535 (fuse_max_max_pages), so the value of this parameter must be > 0 and translate to at most 65535 pages. Additionally, on GKE, the system-wide setting is capped to 16 MiB (16384 KiB) by default by the CSI driver. If needed to be set beyond that on GKE, the user has to manually increase the value on the node before GCSFuse mounting begins. Requires enable-kernel-reader to be set to true, otherwise the value will be ignored.") if err := flagSet.MarkHidden("fuse-max-request-size-kb"); err != nil { return err diff --git a/cfg/params.yaml b/cfg/params.yaml index fb4bf29715..6cc6c32002 100644 --- a/cfg/params.yaml +++ b/cfg/params.yaml @@ -452,14 +452,15 @@ params: type: "int" usage: >- Sets the target maximum request size in KiB that FUSE can process in a single request - (currently used to control read requests only). This is translated to the kernel max_pages + (currently used to control read requests only). This parameter only takes effect when + enable-kernel-reader is set to true. This is translated to the kernel max_pages limit based on host page size. As max_pages_limit is a global, machine-level configuration across all mounts, the host's limit is only updated if the calculated pages value is greater than the current system limit. Note that the FUSE kernel max_pages limit can be set to at most 65535 (fuse_max_max_pages), so the value of this parameter must be > 0 and translate to at most 65535 pages. Additionally, on GKE, the system-wide setting is capped to 16 MiB (16384 KiB) by default by the CSI driver. If needed to be set beyond that on GKE, the user has to manually increase the value on the node before - GCSFuse mounting begins. + GCSFuse mounting begins. Requires enable-kernel-reader to be set to true, otherwise the value will be ignored. default: "StorageClassRapid.DefaultFuseMaxRequestSizeKb()" hide-flag: true optimizations: diff --git a/cmd/mount.go b/cmd/mount.go index 8b2e44a63b..191268d97f 100644 --- a/cmd/mount.go +++ b/cmd/mount.go @@ -202,7 +202,7 @@ func getFuseMountConfig(fsName string, newConfig *cfg.Config) *fuse.MountConfig EnableAsyncReads: newConfig.FileSystem.EnableKernelReader, } - if newConfig.FileSystem.FuseMaxRequestSizeKb > 0 { + if newConfig.FileSystem.EnableKernelReader && newConfig.FileSystem.FuseMaxRequestSizeKb > 0 { mountCfg.MaxPages = uint16(cfg.MaxPagesForRequestSizeKb(int(newConfig.FileSystem.FuseMaxRequestSizeKb))) mountCfg.MaxWrite = uint32(util.MiB) } diff --git a/cmd/mount_test.go b/cmd/mount_test.go index ac74b6bb97..d239f3ca4c 100644 --- a/cmd/mount_test.go +++ b/cmd/mount_test.go @@ -18,6 +18,7 @@ import ( "testing" "github.com/googlecloudplatform/gcsfuse/v3/cfg" + "github.com/googlecloudplatform/gcsfuse/v3/internal/util" "github.com/stretchr/testify/assert" ) @@ -148,3 +149,52 @@ func TestGetFuseMountConfig_EnableReaddirplus(t *testing.T) { }) } } + +func TestGetFuseMountConfig_MaxPagesAndMaxWrite(t *testing.T) { + testCases := []struct { + name string + enableKernelReader bool + fuseMaxRequestSizeKb int64 + expectedMaxPages uint16 + expectedMaxWrite uint32 + }{ + { + name: "KernelReaderDisabled_MaxRequestSizeSet", + enableKernelReader: false, + fuseMaxRequestSizeKb: 16384, + expectedMaxPages: 0, + expectedMaxWrite: 0, + }, + { + name: "KernelReaderEnabled_MaxRequestSizeZero", + enableKernelReader: true, + fuseMaxRequestSizeKb: 0, + expectedMaxPages: 0, + expectedMaxWrite: 0, + }, + { + name: "KernelReaderEnabled_MaxRequestSizeSet", + enableKernelReader: true, + fuseMaxRequestSizeKb: 16384, + expectedMaxPages: uint16(cfg.MaxPagesForRequestSizeKb(16384)), + expectedMaxWrite: uint32(util.MiB), + }, + } + + fsName := "mybucket" + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + newConfig := &cfg.Config{ + FileSystem: cfg.FileSystemConfig{ + EnableKernelReader: tc.enableKernelReader, + FuseMaxRequestSizeKb: tc.fuseMaxRequestSizeKb, + }, + } + + fuseMountCfg := getFuseMountConfig(fsName, newConfig) + + assert.Equal(t, tc.expectedMaxPages, fuseMountCfg.MaxPages) + assert.Equal(t, tc.expectedMaxWrite, fuseMountCfg.MaxWrite) + }) + } +} From 933804bd111216f51aa57928d4e3cbc4503d2f7b Mon Sep 17 00:00:00 2001 From: Abhishek Gupta Date: Fri, 17 Jul 2026 09:07:23 +0000 Subject: [PATCH 5/5] nit change --- .../write_large_files/max_request_size_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/integration_tests/write_large_files/max_request_size_test.go b/tools/integration_tests/write_large_files/max_request_size_test.go index a2780652f2..00208ff9b4 100644 --- a/tools/integration_tests/write_large_files/max_request_size_test.go +++ b/tools/integration_tests/write_large_files/max_request_size_test.go @@ -29,7 +29,7 @@ import ( const ( writeFileStartMsg = "<- WriteFile" - sixteenMiB = 16 * 1024 * 1024 + sixteenMiB = 16 * operations.MiB oneMiBInBytes = "1048576" )