ceph: make nearfull sync writes opt-in

The kernel CephFS client has historically treated a cluster or pool
NEARFULL condition as a request to force successful writes through
generic_write_sync().  That effectively turns otherwise buffered writes
into synchronous writes and can cause a severe throughput drop as soon
as a single OSD or the file data pool crosses the nearfull threshold.

On modern large clusters, NEARFULL is primarily an operator health
signal rather than an immediate client-side capacity failure.  Operators
can still have substantial usable capacity while a cluster is
rebalancing, splitting PGs, or expanding onto new devices.  RBD, RGW and
the userspace CephFS client do not impose this extra client-side
sync-write throttle, so the kernel client behavior is surprising and
operationally painful.

Change the default behavior so NEARFULL no longer changes normal
write-sync semantics.  FULL and pool FULL still fail with -ENOSPC, and
explicitly synchronous writes continue to be synced by
generic_write_sync().

Add a nearfull_sync mount option for deployments that want the legacy
backpressure behavior.  When this option is set, successful writes are
promoted to IOCB_DSYNC if the cluster or file data pool is marked
NEARFULL, preserving the old behavior for conservative deployments.

Link: https://tracker.ceph.com/issues/74849
Signed-off-by: Alex Markuze <amarkuze@redhat.com>
Reviewed-by: Xiubo Li <xiubo.li@clyso.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
This commit is contained in:
Alex Markuze
2026-07-06 13:11:27 +00:00
committed by Ilya Dryomov
parent e7d7aa7b73
commit 5f074d7f29
4 changed files with 22 additions and 3 deletions

View File

@@ -194,6 +194,12 @@ Mount Options
copies. Currently, it's only used in copy_file_range, which will revert
to the default VFS implementation if this option is used.
nearfull_sync
Force written data to stable storage when the cluster or file data pool is
marked NEARFULL. This restores the legacy client-side backpressure
behavior. By default, CephFS writes are not forced synchronous solely
because of NEARFULL.
recover_session=<no|clean>
Set auto reconnect mode in the case where the client is blocklisted. The
available modes are "no" and "clean". The default is "no".

View File

@@ -2388,7 +2388,8 @@ static ssize_t ceph_splice_read(struct file *in, loff_t *ppos,
* dropping our cap refs and allowing the pending snap to logically
* complete _before_ this write occurs.
*
* If we are near ENOSPC, write synchronously.
* If requested, nearfull writes are synced to preserve the legacy
* client-side backpressure behavior.
*/
static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
@@ -2604,8 +2605,9 @@ static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from)
}
if (written >= 0) {
if ((map_flags & CEPH_OSDMAP_NEARFULL) ||
(pool_flags & CEPH_POOL_FLAG_NEARFULL))
if (ceph_test_mount_opt(fsc, NEARFULL_SYNC) &&
((map_flags & CEPH_OSDMAP_NEARFULL) ||
(pool_flags & CEPH_POOL_FLAG_NEARFULL)))
iocb->ki_flags |= IOCB_DSYNC;
written = generic_write_sync(iocb, written);
}

View File

@@ -177,6 +177,7 @@ enum {
Opt_wsync,
Opt_pagecache,
Opt_sparseread,
Opt_nearfull_sync,
};
enum ceph_recover_session_mode {
@@ -205,6 +206,7 @@ static const struct fs_parameter_spec ceph_mount_parameters[] = {
fsparam_flag_no ("ino32", Opt_ino32),
fsparam_string ("mds_namespace", Opt_mds_namespace),
fsparam_string ("mon_addr", Opt_mon_addr),
fsparam_flag_no ("nearfull_sync", Opt_nearfull_sync),
fsparam_flag_no ("poolperm", Opt_poolperm),
fsparam_flag_no ("quotadf", Opt_quotadf),
fsparam_u32 ("rasize", Opt_rasize),
@@ -593,6 +595,12 @@ static int ceph_parse_mount_param(struct fs_context *fc,
else
fsopt->flags |= CEPH_MOUNT_OPT_SPARSEREAD;
break;
case Opt_nearfull_sync:
if (result.negated)
fsopt->flags &= ~CEPH_MOUNT_OPT_NEARFULL_SYNC;
else
fsopt->flags |= CEPH_MOUNT_OPT_NEARFULL_SYNC;
break;
case Opt_test_dummy_encryption:
#ifdef CONFIG_FS_ENCRYPTION
fscrypt_free_dummy_policy(&fsopt->dummy_enc_policy);
@@ -749,6 +757,8 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root)
seq_puts(m, ",nopagecache");
if (fsopt->flags & CEPH_MOUNT_OPT_SPARSEREAD)
seq_puts(m, ",sparseread");
if (fsopt->flags & CEPH_MOUNT_OPT_NEARFULL_SYNC)
seq_puts(m, ",nearfull_sync");
fscrypt_show_test_dummy_encryption(m, ',', root->d_sb);

View File

@@ -45,6 +45,7 @@
#define CEPH_MOUNT_OPT_ASYNC_DIROPS (1<<15) /* allow async directory ops */
#define CEPH_MOUNT_OPT_NOPAGECACHE (1<<16) /* bypass pagecache altogether */
#define CEPH_MOUNT_OPT_SPARSEREAD (1<<17) /* always do sparse reads */
#define CEPH_MOUNT_OPT_NEARFULL_SYNC (1<<18) /* sync writes when nearfull */
#define CEPH_MOUNT_OPT_DEFAULT \
(CEPH_MOUNT_OPT_DCACHE | \