From 3483556684a435e71aa69f5d8575ce5e9b437bb7 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Fri, 6 Sep 2024 00:29:30 -0400 Subject: [PATCH] LU-12515 ofd: allow setting a target to readonly Add a control to toggle read-only mode for an OFD to have it reject all mutating commands with -EROFS This can be used to temporarily set a device to readonly mode while identifying and correcting a misbehaving client. As this prevents clients from destaging data it should not be kept in readonly mode for too long else clients will eventually run out of kernel memory. Example: lctl set_param obdfilter.lustre-OST0000.readonly=1 Test-Parameters: trivial Signed-off-by: Ronnie Sahlberg Change-Id: Ia6658fb58aea17624d5c2ef2528696c4355e7b05 Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/56304 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Andreas Dilger Reviewed-by: Alex Zhuravlev Reviewed-by: Oleg Drokin --- lustre/ofd/lproc_ofd.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++ lustre/ofd/ofd_internal.h | 3 ++- lustre/ofd/ofd_trans.c | 6 ++++++ lustre/tests/sanity.sh | 27 +++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 1 deletion(-) diff --git a/lustre/ofd/lproc_ofd.c b/lustre/ofd/lproc_ofd.c index 91e7094..15b4934 100644 --- a/lustre/ofd/lproc_ofd.c +++ b/lustre/ofd/lproc_ofd.c @@ -347,6 +347,56 @@ LUSTRE_RW_ATTR(no_precreate); #endif /** + * Show if the OFD is in read-only mode. + * + * This means OFD has been adminstratively disabled at the OST to prevent + * writing to the OFD. + * + * \retval number of bytes written + */ +static ssize_t readonly_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct obd_device *obd = container_of(kobj, struct obd_device, + obd_kset.kobj); + struct ofd_device *ofd = ofd_dev(obd->obd_lu_dev); + + return scnprintf(buf, PAGE_SIZE, "%u\n", ofd->ofd_readonly); +} + +/** + * Set OFD to readonly mode. + * + * This is used to interface to userspace administrative tools to + * set the OST read-only. + * + * \param[in] count \a buffer length + * + * \retval \a count on success + * \retval negative number on error + */ +static ssize_t readonly_store(struct kobject *kobj, struct attribute *attr, + const char *buffer, size_t count) +{ + struct obd_device *obd = container_of(kobj, struct obd_device, + obd_kset.kobj); + struct ofd_device *ofd = ofd_dev(obd->obd_lu_dev); + bool val; + int rc; + + rc = kstrtobool(buffer, &val); + if (rc) + return rc; + + spin_lock(&ofd->ofd_flags_lock); + ofd->ofd_readonly = val; + spin_unlock(&ofd->ofd_flags_lock); + + return count; +} +LUSTRE_RW_ATTR(readonly); + +/** * Show OFD filesystem type. * * \param[in] m seq_file handle @@ -1023,6 +1073,7 @@ static struct attribute *ofd_attrs[] = { &lustre_attr_job_cleanup_interval.attr, &lustre_attr_lfsck_speed_limit.attr, &lustre_attr_no_create.attr, + &lustre_attr_readonly.attr, #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 20, 53, 0) &lustre_attr_no_precreate.attr, #endif diff --git a/lustre/ofd/ofd_internal.h b/lustre/ofd/ofd_internal.h index 8670136..730d86f 100644 --- a/lustre/ofd/ofd_internal.h +++ b/lustre/ofd/ofd_internal.h @@ -142,7 +142,8 @@ struct ofd_device { ofd_lastid_rebuilding:1, ofd_record_fid_accessed:1, ofd_lfsck_verify_pfid:1, - ofd_skip_lfsck:1; + ofd_skip_lfsck:1, + ofd_readonly:1; struct seq_server_site ofd_seq_site; /* the limit of SOFT_SYNC RPCs that will trigger a soft sync */ unsigned int ofd_soft_sync_limit; diff --git a/lustre/ofd/ofd_trans.c b/lustre/ofd/ofd_trans.c index 367ca24..963de2e 100644 --- a/lustre/ofd/ofd_trans.c +++ b/lustre/ofd/ofd_trans.c @@ -61,6 +61,12 @@ struct thandle *ofd_trans_create(const struct lu_env *env, LASSERT(info); + if (unlikely(ofd->ofd_readonly)) { + CERROR("%s: Deny transaction for read-only OFD device\n", + ofd_name(ofd)); + return ERR_PTR(-EROFS); + } + th = dt_trans_create(env, ofd->ofd_osd); if (IS_ERR(th)) return th; diff --git a/lustre/tests/sanity.sh b/lustre/tests/sanity.sh index 945a0fb..0350bed 100755 --- a/lustre/tests/sanity.sh +++ b/lustre/tests/sanity.sh @@ -32645,6 +32645,33 @@ test_802b() { } run_test 802b "be able to set MDTs to readonly" +test_802c() { + [ $PARALLEL == "yes" ] && skip "skip parallel run" + + do_facet ost1 $LCTL get_param obdfilter.*.readonly || + skip "readonly option not available" + + $LFS mkdir -i 0 -c 1 $DIR/$tdir || error "(1) fail to mkdir" + + cp $LUSTRE/tests/test-framework.sh $DIR/$tdir/ || + error "(2) Fail to copy" + + # write back all cached data before setting OFD to readonly + cancel_lru_locks + sync_all_data + + do_facet ost1 $LCTL set_param obdfilter.*.readonly=1 + stack_trap "do_facet ost1 $LCTL set_param obdfilter.*.readonly=0" EXIT + + echo "Modify should be refused" + touch $DIR/$tdir/guard && error "(6) Touch should fail under ro mode" + + echo "Read should be allowed" + diff $LUSTRE/tests/test-framework.sh $DIR/$tdir/test-framework.sh || + error "(7) Read should succeed under ro mode" +} +run_test 802c "be able to set OFDs to readonly" + test_803a() { [[ $MDSCOUNT -lt 2 ]] && skip_env "needs >= 2 MDTs" [ $MDS1_VERSION -lt $(version_code 2.10.54) ] && -- 1.8.3.1