Whamcloud - gitweb
LU-17920 mgs: handle compound permanent parameters 18/56218/11
authorEtienne AUJAMES <etienne.aujames@cea.fr>
Fri, 30 Aug 2024 17:46:11 +0000 (19:46 +0200)
committerOleg Drokin <green@whamcloud.com>
Tue, 27 May 2025 04:04:15 +0000 (04:04 +0000)
Special parameters like "nrs_tbf_rule" or "pcc" can "append" several
values.

e.g: add several TBF rules for ost_io
  # lctl set_param ost.OSS.ost_io.nrs_policies="tbf jobid"
  # lctl set_param ost.OSS.ost_io.nrs_tbf_rule="start login
    jobid={*login*} rate=5000"
  # lctl set_param ost.OSS.ost_io.nrs_tbf_rule="start rbh
    jobid={*rbh*} rate=100000"

Multiple permanent values are not supported for these parameters since
the it is possible to set a single value today.
The current implementation will only stores the last value and remove
the old records.

This patch allows "pcc", "nrs_tbf_rule" and future "wbc" to store
several values for the same parameter: it appends new llog records
without deleting the old ones.

If the MGS found an existing record matching the new value, it will
not append the new one.

To remove from the configuration only one entry, the patch modifies
the behavior of "lfs set_param -Pd" and "lfs conf_param -d" to match
the parameter name and the value if set. Those commands return
-ENOENT if the entries are not found.

e.g: remove the TBF rule "rbh" from the configuration
  # lctl set_param -Pd ost.OSS.ost_io.nrs_tbf_rule="start rbh
    jobid={*rbh*} rate=100000"

e.g: remove all TBF rules for ost_io service
  # lctl set_param -Pd ost.OSS.ost_io.nrs_tbf_rule

Add regression test conf-sanity 123aj.
Fix conf-sanity 123ag to use "lctl set_param -P -d" with the value set
in configuration (not "ANY").

Fix sanity-sec 27a to use "lctl set_param -P -d" only if the parameter
have been set before.

Test-Parameters: testlist=conf-sanity env=ONLY=123
Test-Parameters: testlist=conf-sanity env=ONLY=123
Test-Parameters: testlist=conf-sanity env=ONLY=123aj,ONLY_REPEAT=20
Test-Parameters: testlist=sanity env=ONLY=400b
Test-Parameters: testlist=sanity env=ONLY=401db,ONLY_REPEAT=20
Signed-off-by: Etienne AUJAMES <eaujames@ddn.com>
Change-Id: I9d3ec3d8d9004218138468739d4f7c5ea8a3eadd
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/56218
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Qian Yingjin <qian@ddn.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/include/lustre_log.h
lustre/include/uapi/linux/lustre/lustre_disk.h
lustre/include/uapi/linux/lustre/lustre_param.h
lustre/mgs/mgs_llog.c
lustre/obdclass/llog.c
lustre/tests/conf-sanity.sh
lustre/tests/sanity-sec.sh
lustre/utils/lustre_cfg.c

index e692249..31d0305 100644 (file)
@@ -600,6 +600,10 @@ int llog_open_create(const struct lu_env *env, struct llog_ctxt *ctxt,
                     char *name);
 int llog_erase(const struct lu_env *env, struct llog_ctxt *ctxt,
               struct llog_logid *logid, char *name);
+int llog_get_cookie(const struct lu_env *env, struct llog_cookie *out);
+int llog_write_cookie(const struct lu_env *env, struct llog_handle *loghandle,
+                     struct llog_rec_hdr *rec, struct llog_cookie *cookie,
+                     int idx);
 int llog_write(const struct lu_env *env, struct llog_handle *loghandle,
               struct llog_rec_hdr *rec, int idx);
 
index a39c436..253686d 100644 (file)
@@ -109,7 +109,7 @@ struct lustre_disk_data {
 #define LDD_F_IR_CAPABLE       0x2000
 /** the MGS refused to register the target. */
 #define LDD_F_ERROR            0x4000
-/** process at lctl conf_param */
+/** process at lctl set_param */
 #define LDD_F_PARAM2           0x8000
 /** the target shouldn't use local logs */
 #define LDD_F_NO_LOCAL_LOGS    0x10000
index 1714f76..2a219e7 100644 (file)
@@ -18,6 +18,8 @@
 #ifndef _UAPI_LUSTRE_PARAM_H
 #define _UAPI_LUSTRE_PARAM_H
 
+#include <linux/string.h>
+
 /** \defgroup param param
  *
  * @{
 #define PARAM_NOSQUASHNIDS        "nosquash_nids="    /* no squash nids */
 #define PARAM_AUTODEGRADE         "autodegrade="      /* autodegrade OST's */
 
+/* compound parameters */
+#define PARAM_TBFRULES          "nrs_tbf_rule="        /* start|change|stop tbf rule */
+#define PARAM_PCC              "pcc="          /* add|del|clear pcc datasets */
+#define PARAM_WBC              "wbc="          /* Metadata writeback cache */
+
 /* Prefixes for parameters handled by obd's proc methods (XXX_process_config) */
 #define PARAM_OST              "ost."
 #define PARAM_OSD              "osd."
index 4c542f5..1934e27 100644 (file)
@@ -1061,6 +1061,294 @@ out_pop:
        RETURN(rc);
 }
 
+static int param_value_cmp(const char *val1, const char *val2)
+{
+       int rc = 0;
+
+       if (!val1 || !val2)
+               return val1 != val2;
+
+       do {
+               size_t len1, len2;
+
+               val1 += strspn(val1, " ");
+               val2 += strspn(val2, " ");
+               len1 = strcspn(val1, " ");
+               len2 = strcspn(val2, " ");
+               if (len1 != len2) {
+                       rc = 1;
+                       break;
+               } else if (!len1) {
+                       break;
+               }
+
+               rc = (strncmp(val1, val2, len1) != 0);
+               val1 += len1;
+               val2 += len2;
+       } while (!rc);
+
+       return rc;
+}
+
+enum mgs_modify_param_mode {
+       MPM_REPLACE = 0,
+       MPM_DELETE,
+       MPM_LOOKUP_VAL,
+};
+
+struct mgs_modify_param_data {
+       enum mgs_modify_param_mode mpm_mode;
+       __s64                   mpm_canceltime;
+       char                    *mpm_tgt;
+       char                    *mpm_name;
+       char                    *mpm_value;
+       struct llog_rec_hdr     *mpm_start;
+       struct llog_cookie      mpm_start_cookie;
+       int                     mpm_deleted;
+};
+
+static int mgs_modify_param_hdl(const struct lu_env *env,
+                               struct llog_handle *llh,
+                               struct llog_rec_hdr *rec, void *data)
+{
+       struct mgs_modify_param_data *mpm = data;
+       struct lustre_cfg *lcfg = REC_DATA(rec);
+       int cfg_len = REC_DATA_LEN(rec);
+       char *name;
+       char *value;
+       bool value_match;
+       int rc;
+
+       ENTRY;
+       if (rec->lrh_type != OBD_CFG_REC) {
+               CERROR("%s: unhandled lrh_type %#x: rc = %d\n",
+                      llh->lgh_name, rec->lrh_type, -EINVAL);
+               RETURN(-EINVAL);
+       }
+
+       rc = lustre_cfg_sanity_check(lcfg, cfg_len);
+       if (rc) {
+               CERROR("%s: Insane config: rc = %d\n", llh->lgh_name, rc);
+               RETURN(rc);
+       }
+
+       /* We only care about markers */
+       if (lcfg->lcfg_command == LCFG_MARKER) {
+               struct cfg_marker *marker = lustre_cfg_buf(lcfg, 1);
+
+               if (!(marker->cm_flags & (CM_START | CM_END)) ||
+                   (marker->cm_flags & CM_SKIP))
+                       RETURN(0);
+
+               if (strcmp(mpm->mpm_name, marker->cm_comment) != 0 ||
+                   strcmp(mpm->mpm_tgt, marker->cm_tgtname) != 0)
+                       RETURN(0);
+
+               marker->cm_flags |= CM_SKIP;
+               marker->cm_canceltime = mpm->mpm_canceltime;
+               if (marker->cm_flags & CM_START) {
+                       /* missing end section */
+                       if (unlikely(mpm->mpm_start))
+                               GOTO(free_mdp, rc = -EBADMSG);
+
+                       /* save the start marker to skip */
+                       rc = llog_get_cookie(env, &mpm->mpm_start_cookie);
+                       if (rc)
+                               RETURN(rc);
+
+                       OBD_ALLOC(mpm->mpm_start, rec->lrh_len);
+                       if (!mpm->mpm_start)
+                               RETURN(-ENOMEM);
+
+                       memcpy(mpm->mpm_start, rec, rec->lrh_len);
+
+                       RETURN(0);
+               }
+
+               if (!mpm->mpm_start)
+                       RETURN(0);
+               if (mpm->mpm_mode == MPM_LOOKUP_VAL)
+                       GOTO(free_mdp, rc = 0);
+
+               /* skip the param section : re-write CM_START marker */
+               rc = llog_write_cookie(env, llh, mpm->mpm_start,
+                                      &mpm->mpm_start_cookie,
+                                      mpm->mpm_start->lrh_index);
+               if (rc < 0)
+                       GOTO(free_mdp, rc);
+               /* re-write CM_END marker */
+               rc = llog_write(env, llh, rec, rec->lrh_index);
+               if (rc)
+                       GOTO(free_mdp, rc);
+
+               mpm->mpm_deleted++;
+               if (mpm->mpm_mode == MPM_REPLACE)
+                       rc = LLOG_PROC_BREAK;
+
+               GOTO(free_mdp, rc);
+       }
+
+       if (!mpm->mpm_start)
+               RETURN(0);
+       if (lcfg->lcfg_command != LCFG_PARAM &&
+           lcfg->lcfg_command != LCFG_SET_PARAM)
+               RETURN(0);
+
+       /* check old parameter value */
+       value = lustre_cfg_string(lcfg, 1);
+       name = strsep(&value, "=");
+
+       /* param does not match with marker comment -> ignore */
+       if (unlikely(strcmp(mpm->mpm_name, name) != 0))
+               GOTO(free_mdp, rc = 0);
+       /* no value to check */
+       if (!mpm->mpm_value)
+               RETURN(0);
+
+       value_match = value && !param_value_cmp(mpm->mpm_value, value);
+
+       switch (mpm->mpm_mode) {
+       case MPM_DELETE:
+               if (!value_match)
+                       GOTO(free_mdp, rc = 0);
+               break;
+       case MPM_REPLACE:
+       case MPM_LOOKUP_VAL:
+               if (value_match)
+                       GOTO(free_mdp, rc = -EEXIST);
+               break;
+       default:
+               RETURN(-EINVAL);
+       }
+
+       RETURN(0);
+
+free_mdp:
+       OBD_FREE(mpm->mpm_start, mpm->mpm_start->lrh_len);
+       mpm->mpm_start = NULL;
+
+       return rc;
+}
+
+static int param_is_compound(const char *param)
+{
+       size_t len;
+       const char *str = param;
+
+       len = strcspn(str, ".=");
+       while (str[len] == '.') {
+               str += len + 1;
+               len = strcspn(str, ".=");
+       }
+
+       if (!len || str[len] != '=')
+               return 0;
+
+       len++;
+       if (strncmp(str, PARAM_TBFRULES, len) == 0 ||
+           strncmp(str, PARAM_PCC, len) == 0 ||
+           strncmp(str, PARAM_WBC, len) == 0)
+               return 1;
+
+       return 0;
+}
+
+/**
+ * Mark param section with CM_SKIP matching param name and value
+ * Return code:
+ * 0 - deleted successfully,
+ * 1 - no modification was done
+ * negative - error
+ */
+static int mgs_modify_param(const struct lu_env *env, struct mgs_device *mgs,
+                           struct fs_db *fsdb, struct mgs_target_info *mti,
+                           char *logname, char *devname, char *param, bool del)
+{
+       struct llog_handle *loghandle;
+       struct llog_ctxt *ctxt;
+       struct mgs_modify_param_data mpm = { 0 };
+       char *dup;
+       int rc;
+
+       ENTRY;
+
+       LASSERT(mutex_is_locked(&fsdb->fsdb_mutex));
+       CDEBUG(D_MGS, "modify %s/%s/%s del=%d\n", logname, devname, param, del);
+
+       if (!param || !param[0])
+               RETURN(-EINVAL);
+
+       ctxt = llog_get_context(mgs->mgs_obd, LLOG_CONFIG_ORIG_CTXT);
+       LASSERT(ctxt != NULL);
+       rc = llog_open(env, ctxt, &loghandle, NULL, logname, LLOG_OPEN_EXISTS);
+       if (rc < 0) {
+               if (rc == -ENOENT)
+                       rc = 0;
+               GOTO(out_pop, rc);
+       }
+
+       rc = llog_init_handle(env, loghandle, LLOG_F_IS_PLAIN, NULL);
+       if (rc)
+               GOTO(out_close, rc);
+
+       if (llog_get_size(loghandle) <= 1)
+               GOTO(out_close, rc = 0);
+
+       dup = kstrdup(param, GFP_KERNEL);
+       if (!dup)
+               GOTO(out_close, rc = -ENOMEM);
+
+       mpm.mpm_value = dup;
+       mpm.mpm_name = strsep(&mpm.mpm_value, "=");
+       if (mpm.mpm_value && !mpm.mpm_value[0])
+               mpm.mpm_value = NULL;
+
+       /* for compound parameter several entries are allowed, only
+        * check for existing entries matching name and value.
+        */
+       if (del)
+               mpm.mpm_mode = MPM_DELETE;
+       else if (mpm.mpm_value)
+               mpm.mpm_mode = param_is_compound(param) ?
+                       MPM_LOOKUP_VAL : MPM_REPLACE;
+       else
+               GOTO(out_free, rc = EINVAL);
+
+       mpm.mpm_canceltime = ktime_get_real_seconds();
+       mpm.mpm_tgt = devname;
+
+       rc = llog_process(env, loghandle, mgs_modify_param_hdl, &mpm, NULL);
+       if (unlikely(mpm.mpm_start)) {
+               OBD_FREE(mpm.mpm_start, mpm.mpm_start->lrh_len);
+               GOTO(out_free, rc = -EBADMSG);
+       }
+
+       if (rc == LLOG_PROC_BREAK)
+               rc = 0;
+       if (rc)
+               GOTO(out_free, rc);
+
+       switch (mpm.mpm_mode) {
+       case MPM_DELETE:
+               rc = mpm.mpm_deleted ? 0 : -ENOENT;
+               break;
+       default:
+               rc = !mpm.mpm_deleted;
+       }
+
+out_free:
+       kfree(dup);
+out_close:
+       llog_close(env, loghandle);
+out_pop:
+       if (rc < 0)
+               CWARN("%s: modify %s/%s (mode = %d) failed: rc = %d\n",
+                     mgs->mgs_obd->obd_name, mti->mti_svname, param,
+                     mpm.mpm_mode, rc);
+       llog_ctxt_put(ctxt);
+       RETURN(rc);
+}
+
 enum replace_state {
        REPLACE_COPY = 0,
        REPLACE_SKIP,
@@ -3559,11 +3847,19 @@ out_free:
        RETURN(rc);
 }
 
-static __inline__ int mgs_param_empty(char *ptr)
+static inline void mgs_param_del_value(char *ptr)
 {
        char *tmp = strchr(ptr, '=');
 
-       if (tmp && tmp[1] == '\0')
+       if (tmp && tmp[1])
+               tmp[1] = '\0';
+}
+
+static inline int mgs_param_empty(const char *ptr)
+{
+       char *tmp = strchr(ptr, '=');
+
+       if (!tmp || tmp[1] == '\0')
                return 1;
        return 0;
 }
@@ -3690,33 +3986,24 @@ static int mgs_wlp_lcfg(const struct lu_env *env,
                        struct mgs_device *mgs, struct fs_db *fsdb,
                        struct mgs_target_info *mti,
                        char *logname, struct lustre_cfg_bufs *bufs,
-                       char *tgtname, char *ptr)
+                       char *tgtname, char *ptr, bool del)
 {
-       char comment[MTI_NAME_MAXLEN];
-       char *tmp;
        struct llog_cfg_rec *lcr;
-       int rc, del;
-
-       /* Erase any old settings of this same parameter */
-       strscpy(comment, ptr, sizeof(comment));
-       /* But don't try to match the value. */
-       tmp = strchr(comment, '=');
-       if (tmp != NULL)
-               *tmp = 0;
-       /* FIXME we should skip settings that are the same as old values */
-       rc = mgs_modify(env, mgs, fsdb, mti, logname, tgtname, comment,CM_SKIP);
+       char *comment;
+       int rc, len;
+
+       /* erase any old settings of this same parameter */
+       rc = mgs_modify_param(env, mgs, fsdb, mti, logname, tgtname, ptr, del);
+       /* nothing to do */
+       if (rc == -EEXIST)
+               return 0;
        if (rc < 0)
                return rc;
-       del = mgs_param_empty(ptr);
 
        LCONSOLE_INFO("%s parameter %s.%s in log %s\n", del ? "Disabling" : rc ?
-                     "Setting" : "Modifying", tgtname, comment, logname);
-       if (del) {
-               /* mgs_modify() will return 1 if nothing had to be done */
-               if (rc == 1)
-                       rc = 0;
-               return rc;
-       }
+                     "Setting" : "Modifying", tgtname, ptr, logname);
+       if (del)
+               return 0;
 
        lustre_cfg_bufs_reset(bufs, tgtname);
        lustre_cfg_bufs_set_string(bufs, 1, ptr);
@@ -3728,9 +4015,18 @@ static int mgs_wlp_lcfg(const struct lu_env *env,
        if (lcr == NULL)
                return -ENOMEM;
 
+       len = strcspn(ptr, "=");
+       comment = kstrndup(ptr, len, GFP_KERNEL);
+       if (!comment)
+               GOTO(out, rc = -ENOMEM);
+
        rc = mgs_write_log_direct(env, mgs, fsdb, logname, lcr, tgtname,
                                  comment);
+       kfree(comment);
+
+out:
        lustre_cfg_rec_free(lcr);
+
        return rc;
 }
 
@@ -4225,7 +4521,8 @@ out:
 static int mgs_write_log_param2(const struct lu_env *env,
                                struct mgs_device *mgs,
                                struct fs_db *fsdb,
-                               struct mgs_target_info *mti, char *ptr)
+                               struct mgs_target_info *mti,
+                               char *ptr, bool del)
 {
        struct lustre_cfg_bufs bufs;
        int rc;
@@ -4256,6 +4553,9 @@ static int mgs_write_log_param2(const struct lu_env *env,
         * doesn't transmit to the client. See LU-7183.
         */
        if (!class_match_param(ptr, PARAM_SRPC, NULL)) {
+               if (del)
+                       mgs_param_del_value(ptr);
+
                rc = mgs_srpc_set_param(env, mgs, fsdb, mti, ptr);
                goto end;
        }
@@ -4269,6 +4569,9 @@ static int mgs_write_log_param2(const struct lu_env *env,
                 */
                const char *param;
 
+               if (del)
+                       mgs_param_del_value(ptr);
+
                /* can't use wildcards with failover.node */
                if (strchr(ptr, '*')) {
                        rc = -ENODEV;
@@ -4302,7 +4605,7 @@ static int mgs_write_log_param2(const struct lu_env *env,
        }
 
        rc = mgs_wlp_lcfg(env, mgs, fsdb, mti, PARAMS_FILENAME, &bufs,
-                         mti->mti_svname, ptr);
+                         mti->mti_svname, ptr, del);
 end:
        RETURN(rc);
 }
@@ -4314,7 +4617,7 @@ end:
  */
 static int mgs_write_log_param(const struct lu_env *env,
                               struct mgs_device *mgs, struct fs_db *fsdb,
-                              struct mgs_target_info *mti, char *ptr)
+                              struct mgs_target_info *mti, char *ptr, bool del)
 {
        struct mgs_thread_info *mgi = mgs_env_info(env);
        char *logname;
@@ -4350,6 +4653,9 @@ static int mgs_write_log_param(const struct lu_env *env,
        }
 
        if (class_match_param(ptr, PARAM_SRPC, NULL) == 0) {
+               if (del)
+                       mgs_param_del_value(ptr);
+
                rc = mgs_srpc_set_param(env, mgs, fsdb, mti, ptr);
                GOTO(end, rc);
        }
@@ -4362,17 +4668,26 @@ static int mgs_write_log_param(const struct lu_env *env,
                 */
                if (mti->mti_flags & LDD_F_PARAM) {
                        CDEBUG(D_MGS, "Adding failnode\n");
+                       if (del)
+                               mgs_param_del_value(ptr);
+
                        rc = mgs_write_log_add_failnid(env, mgs, fsdb, mti);
                }
                GOTO(end, rc);
        }
 
        if (class_match_param(ptr, PARAM_SYS, &tmp) == 0) {
+               if (del)
+                       mgs_param_del_value(ptr);
+
                rc = mgs_write_log_sys(env, mgs, fsdb, mti, ptr, tmp);
                GOTO(end, rc);
        }
 
        if (class_match_param(ptr, PARAM_QUOTA, &tmp) == 0) {
+               if (del)
+                       mgs_param_del_value(ptr);
+
                rc = mgs_write_log_quota(env, mgs, fsdb, mti, ptr, tmp);
                GOTO(end, rc);
        }
@@ -4380,7 +4695,7 @@ static int mgs_write_log_param(const struct lu_env *env,
        if (class_match_param(ptr, PARAM_OSC PARAM_ACTIVE, &tmp) == 0 ||
            class_match_param(ptr, PARAM_MDC PARAM_ACTIVE, &tmp) == 0) {
                /* active=0 means off, anything else means on */
-               int flag = (*tmp == '0') ? CM_EXCLUDE : 0;
+               int flag = (*tmp == '0' || del) ? CM_EXCLUDE : 0;
                bool deactive_osc = memcmp(ptr, PARAM_OSC PARAM_ACTIVE,
                                           strlen(PARAM_OSC PARAM_ACTIVE)) == 0;
                int i;
@@ -4462,7 +4777,7 @@ active_err:
                if (rc)
                        GOTO(end, rc);
                rc = mgs_wlp_lcfg(env, mgs, fsdb, mti, mti->mti_svname,
-                                 &mgi->mgi_bufs, mdtlovname, ptr);
+                                 &mgi->mgi_bufs, mdtlovname, ptr, del);
                name_destroy(&logname);
                name_destroy(&mdtlovname);
                if (rc)
@@ -4473,7 +4788,7 @@ active_err:
                if (rc)
                        GOTO(end, rc);
                rc = mgs_wlp_lcfg(env, mgs, fsdb, mti, logname, &mgi->mgi_bufs,
-                                 fsdb->fsdb_clilov, ptr);
+                                 fsdb->fsdb_clilov, ptr, del);
                name_destroy(&logname);
                GOTO(end, rc);
        }
@@ -4526,7 +4841,7 @@ active_err:
                        GOTO(end, rc);
                }
                rc = mgs_wlp_lcfg(env, mgs, fsdb, mti, logname, &mgi->mgi_bufs,
-                                 cname, ptr);
+                                 cname, ptr, del);
 
                /* osc params affect the MDT as well */
                if (!rc && (mti->mti_flags & LDD_F_SV_TYPE_OST)) {
@@ -4549,7 +4864,7 @@ active_err:
                                        rc = mgs_wlp_lcfg(env, mgs, fsdb,
                                                          mti, logname,
                                                          &mgi->mgi_bufs,
-                                                         cname, ptr);
+                                                         cname, ptr, del);
                                        if (rc)
                                                break;
                                }
@@ -4598,7 +4913,8 @@ active_err:
                                        break;
 
                                rc = mgs_wlp_lcfg(env, mgs, fsdb, mti, logname,
-                                                 &mgi->mgi_bufs, cname, ptr);
+                                                 &mgi->mgi_bufs, cname,
+                                                 ptr, del);
                                if (rc < 0)
                                        break;
 
@@ -4618,7 +4934,7 @@ active_err:
 
                                rc = mgs_wlp_lcfg(env, mgs, fsdb, mti, logname,
                                                  &mgi->mgi_bufs, lodname,
-                                                 param_str);
+                                                 param_str, del);
                                if (rc < 0)
                                        break;
                        }
@@ -4659,7 +4975,7 @@ active_err:
                                        goto active_err;
                                rc = mgs_wlp_lcfg(env, mgs, fsdb, mti,
                                                  logname, &mgi->mgi_bufs,
-                                                 logname, ptr);
+                                                 logname, ptr, del);
                                name_destroy(&logname);
                                if (rc)
                                        goto active_err;
@@ -4674,7 +4990,7 @@ active_err:
                        }
                        rc = mgs_wlp_lcfg(env, mgs, fsdb, mti,
                                          mti->mti_svname, &mgi->mgi_bufs,
-                                         mti->mti_svname, ptr);
+                                         mti->mti_svname, ptr, del);
                        if (rc)
                                goto active_err;
                }
@@ -4702,7 +5018,7 @@ active_err:
                                GOTO(end, rc);
                        }
                        rc = mgs_wlp_lcfg(env, mgs, fsdb, mti, logname,
-                                         &mgi->mgi_bufs, cname, ptr2);
+                                         &mgi->mgi_bufs, cname, ptr2, del);
                        name_destroy(&ptr2);
                        name_destroy(&logname);
                        name_destroy(&cname);
@@ -4720,7 +5036,7 @@ active_err:
                        GOTO(end, rc = -ENODEV);
 
                rc = mgs_wlp_lcfg(env, mgs, fsdb, mti, mti->mti_svname,
-                                 &mgi->mgi_bufs, mti->mti_svname, ptr);
+                                 &mgi->mgi_bufs, mti->mti_svname, ptr, del);
                GOTO(end, rc);
        }
 
@@ -4807,7 +5123,9 @@ int mgs_write_log_target(const struct lu_env *env, struct mgs_device *mgs,
                }
                CDEBUG(D_MGS, "remaining string: '%s', param: '%s'\n",
                       params, buf);
-               rc = mgs_write_log_param(env, mgs, fsdb, mti, buf);
+               rc = mgs_write_log_param(env, mgs, fsdb, mti, buf, false);
+               if (rc == -EEXIST)
+                       rc = 0;
                if (rc)
                        break;
        }
@@ -5558,7 +5876,7 @@ int mgs_params_fsdb_cleanup(const struct lu_env *env, struct mgs_device *mgs)
  **/
 static int mgs_set_conf_param(const struct lu_env *env, struct mgs_device *mgs,
                              struct mgs_target_info *mti, const char *devname,
-                             const char *param)
+                             const char *param, bool del)
 {
        struct fs_db *fsdb = NULL;
        int dev_type;
@@ -5664,7 +5982,7 @@ static int mgs_set_conf_param(const struct lu_env *env, struct mgs_device *mgs,
         */
        mti->mti_flags = dev_type | LDD_F_PARAM;
        mutex_lock(&fsdb->fsdb_mutex);
-       rc = mgs_write_log_param(env, mgs, fsdb, mti, mti->mti_params);
+       rc = mgs_write_log_param(env, mgs, fsdb, mti, mti->mti_params, del);
        mutex_unlock(&fsdb->fsdb_mutex);
        mgs_revoke_lock(mgs, fsdb, MGS_CFG_T_CONFIG);
 
@@ -5676,7 +5994,8 @@ out:
 }
 
 static int mgs_set_param2(const struct lu_env *env, struct mgs_device *mgs,
-                         struct mgs_target_info *mti, const char *param)
+                         struct mgs_target_info *mti,
+                         const char *param, bool del)
 {
        struct fs_db *fsdb = NULL;
        int dev_type;
@@ -5763,7 +6082,7 @@ static int mgs_set_param2(const struct lu_env *env, struct mgs_device *mgs,
         */
        mti->mti_flags = dev_type | LDD_F_PARAM2;
        mutex_lock(&fsdb->fsdb_mutex);
-       rc = mgs_write_log_param2(env, mgs, fsdb, mti, mti->mti_params);
+       rc = mgs_write_log_param2(env, mgs, fsdb, mti, mti->mti_params, del);
        mutex_unlock(&fsdb->fsdb_mutex);
        mgs_revoke_lock(mgs, fsdb, MGS_CFG_T_PARAMS);
        mgs_put_fsdb(mgs, fsdb);
@@ -5780,7 +6099,9 @@ int mgs_set_param(const struct lu_env *env, struct mgs_device *mgs,
                  struct lustre_cfg *lcfg)
 {
        const char *param = lustre_cfg_string(lcfg, 1);
+       char *delstr = lustre_cfg_string(lcfg, 2);
        struct mgs_target_info *mti;
+       bool del;
        int rc;
 
        /* Create a fake mti to hold everything */
@@ -5790,18 +6111,20 @@ int mgs_set_param(const struct lu_env *env, struct mgs_device *mgs,
 
        print_lustre_cfg(lcfg);
 
+       /* no value means delete the parameter */
+       del = mgs_param_empty(param) || (delstr && strcmp(delstr, "del") == 0);
        if (lcfg->lcfg_command == LCFG_PARAM) {
                /* For the case of lctl conf_param devname can be
                 * lustre, lustre-mdtlov, lustre-client, lustre-MDT0000
                 */
                const char *devname = lustre_cfg_string(lcfg, 0);
 
-               rc = mgs_set_conf_param(env, mgs, mti, devname, param);
+               rc = mgs_set_conf_param(env, mgs, mti, devname, param, del);
        } else {
                /* In the case of lctl set_param -P lcfg[0] will always
                 * be 'general'. At least for now.
                 */
-               rc = mgs_set_param2(env, mgs, mti, param);
+               rc = mgs_set_param2(env, mgs, mti, param, del);
        }
 
        OBD_FREE_PTR(mti);
index b839e91..982d21b 100644 (file)
@@ -1325,13 +1325,45 @@ int llog_erase(const struct lu_env *env, struct llog_ctxt *ctxt,
 }
 EXPORT_SYMBOL(llog_erase);
 
-/*
- * Helper function for write record in llog.
+/* Get current llog cookie from llog_process_thread session */
+int llog_get_cookie(const struct lu_env *env, struct llog_cookie *out)
+{
+       struct llog_thread_info *lti = llog_info(env);
+
+       if (!out || !lti)
+               return -EINVAL;
+
+       memcpy(out, &lti->lgi_cookie, sizeof(lti->lgi_cookie));
+
+       return 0;
+}
+EXPORT_SYMBOL(llog_get_cookie);
+
+/**
+ * Helper function for write record in llog with a custom cookie.
  * It hides all transaction handling from caller.
  * Valid only with local llog.
+ *
+ * \param[in]  env             execution environment
+ * \param[in]  loghandle       llog handle of the current llog
+ * \param[in]  rec             llog record header. This is a real header of
+ *                             the full llog record to write. This is
+ *                             the beginning of buffer to write, the length
+ *                             of buffer is stored in \a rec::lrh_len
+ * \param[in,out] reccookie    pointer to the cookie to return back if needed.
+ *                             It is used for further cancel of this llog
+ *                             record.
+ * \param[in]  idx             index of the llog record. If \a idx == -1 then
+ *                             this is append case, otherwise \a idx is
+ *                             the index of record to modify
+ *
+ * \retval                     0 on successful write && \a reccookie == NULL
+ *                             1 on successful write && \a reccookie != NULL
+ * \retval                     negative error if write failed
  */
-int llog_write(const struct lu_env *env, struct llog_handle *loghandle,
-              struct llog_rec_hdr *rec, int idx)
+int llog_write_cookie(const struct lu_env *env, struct llog_handle *loghandle,
+                     struct llog_rec_hdr *rec, struct llog_cookie *cookie,
+                     int idx)
 {
        struct dt_device        *dt;
        struct thandle          *th;
@@ -1382,7 +1414,7 @@ int llog_write(const struct lu_env *env, struct llog_handle *loghandle,
        need_cookie = !(idx == LLOG_HEADER_IDX || idx == LLOG_NEXT_IDX);
 
        down_write(&loghandle->lgh_lock);
-       if (need_cookie) {
+       if (need_cookie && !cookie) {
                struct llog_thread_info *lti = llog_info(env);
 
                /* cookie comes from llog_process_thread */
@@ -1391,7 +1423,7 @@ int llog_write(const struct lu_env *env, struct llog_handle *loghandle,
                /* upper layer didn`t pass cookie so change rc */
                rc = (rc == 1 ? 0 : rc);
        } else {
-               rc = llog_write_rec(env, loghandle, rec, NULL, idx, th);
+               rc = llog_write_rec(env, loghandle, rec, cookie, idx, th);
        }
        if (rc == 0 && update_attr) {
                loghandle->lgh_timestamp = timestamp;
@@ -1405,6 +1437,13 @@ out_trans:
        dt_trans_stop(env, dt, th);
        RETURN(rc);
 }
+EXPORT_SYMBOL(llog_write_cookie);
+
+int llog_write(const struct lu_env *env, struct llog_handle *loghandle,
+              struct llog_rec_hdr *rec, int idx)
+{
+       return llog_write_cookie(env, loghandle, rec, NULL, idx);
+}
 EXPORT_SYMBOL(llog_write);
 
 int llog_open(const struct lu_env *env, struct llog_ctxt *ctxt,
index 8932996..ca1a1bc 100755 (executable)
@@ -10763,7 +10763,7 @@ test_123ag() { # LU-15142
                grep -c jobid_name)
        (( rec == 1)) || error "parameter is not set"
        # usage with ordinary set_param format works too
-       do_facet mgs $LCTL set_param -P -d jobid_name="ANY"
+       do_facet mgs $LCTL set_param -P -d jobid_name="TESTNAME1"
        rec=$(do_facet mgs $LCTL --device MGS llog_print params |
                grep -c jobid_name)
        (( rec == 0 )) || error "parameter was not deleted, check #2"
@@ -10874,6 +10874,114 @@ test_123ai() { #LU-16167
 }
 run_test 123ai "llog_print display all non skipped records"
 
+cleanup_123aj() {
+       local svc=$1
+
+       echo "cleanup test 123aj"
+
+       # cancel last TBF parameter records
+       do_facet mgs "$LCTL set_param -P -d  ${svc}.nrs_tbf_rule" || true
+       do_facet mgs "$LCTL set_param -P -d  ${svc}.nrs_policies" || true
+
+       # restore old NRS policy
+       do_nodes $(comma_list $(osts_nodes)) \
+               "$LCTL set_param ${svc}.nrs_policies=fifo"
+}
+
+check_compound_param_val() {
+       local value llog_print
+       local svc=$1
+       shift
+
+       llog_print=$(do_facet mgs "$LCTL llog_print params") ||
+               error "fail to read CONFIG/params"
+
+       for value in "$@"; do
+               local n=$(grep -c "${svc}.*$value }$" <<< "$llog_print")
+
+               (( n > 0 )) || error "fail to found '$value' in params config"
+               (( n == 1 )) || error "several records found ($n) for '$value'"
+       done
+}
+
+add_random_spaces()
+{
+       local word str
+       local spaces=$(printf '%*s' $(($RANDOM % 4)) '')
+
+       str=$spaces
+       for word in $*; do
+               spaces=$(printf '%*s' $(($RANDOM % 4)) '')
+               str+="$word $spaces"
+       done
+
+       echo "${str:0:-1}"
+}
+
+test_123aj() { #LU-17920
+       local old_policy
+       local key rule
+       local llog_print
+       local -A tbf_rules
+       local ost_io="ost.OSS.ost_io"
+
+       tbf_rules[rule1]="start rule1 uid={500 502 } rate=1000"
+       tbf_rules[rule2]="start rule2 nid={10.10.2.20@tcp} rate=1000"
+       tbf_rules[rule3]="start rule3 jobid={cat.0.0} rate=1000"
+       tbf_rules[rule4]="start rule4 jobid={fio.0.0} rate=500"
+       tbf_rules[change4_1]="change rule4 rate=50"
+       tbf_rules[change4_2]="change rule4 rate=5000"
+
+       remote_mgs_nodsh && skip "remote MGS with nodsh"
+       (( MGS_VERSION >= $(version_code v2_16_54-23) )) ||
+               skip "need MGS >= v2_16_54-23 for compound llog records"
+
+       [ -d $MOUNT/.lustre ] || setup
+
+       stack_trap "cleanup_123aj $ost_io"
+
+       do_facet mgs "$LCTL set_param -P ${ost_io}.nrs_policies=tbf" ||
+               error "fail to set nrs_policies=tbf on MGS"
+
+       for rule in "${tbf_rules[@]}"; do
+               do_facet mgs "$LCTL set_param -P ${ost_io}.nrs_tbf_rule='$rule'" ||
+                       error "fail to set nrs_tbf_rule='$rule' on MGS"
+
+               # the rec should be added only the first time
+               rule="$(add_random_spaces "$rule")"
+               do_facet mgs "$LCTL set_param -P ${ost_io}.nrs_tbf_rule='$rule'" ||
+                       error "fail to set nrs_tbf_rule='$rule' on MGS"
+       done
+       check_compound_param_val $ost_io "${tbf_rules[@]}"
+
+       # remove all the rules
+       do_facet mgs "$LCTL set_param -P -d ${ost_io}.nrs_tbf_rule" ||
+               error "fail to remove all the TBF rules"
+
+       llog_print=$(do_facet mgs "$LCTL llog_print params") ||
+               error "fail to read CONFIG/params"
+       ! grep -q nrs_tbf_rule <<< "$llog_print" ||
+               error "fail to remove all the TBF rules"
+
+       # re-add the rules
+       for rule in "${tbf_rules[@]}"; do
+               do_facet mgs "$LCTL set_param -P ${ost_io}.nrs_tbf_rule='$rule'" ||
+                       error "fail to set nrs_tbf_rule='$rule' on MGS"
+       done
+       check_compound_param_val $ost_io "${tbf_rules[@]}"
+
+       # shuffle and remove the rule one by one
+       printf "%s\n" "${!tbf_rules[@]}" | sort -R |
+               while read key; do
+                       rule="${tbf_rules[$key]}"
+                       unset tbf_rules[$key]
+                       do_facet mgs "$LCTL set_param -P -d ${ost_io}.nrs_tbf_rule='$rule'" ||
+                               error "fail to set nrs_tbf_rule='$rule' on MGS"
+                       check_compound_param_val $ost_io "${tbf_rules[@]}"
+               done
+}
+run_test 123aj "check permanent TBF rules"
+
 test_123_prep() {
        remote_mgs_nodsh && skip "remote MGS with nodsh"
 
index f2fdf72..91271cf 100755 (executable)
@@ -2286,8 +2286,10 @@ nodemap_exercise_fileset() {
 
        # check whether fileset was removed on remote nodes
        wait_nm_sync $nm fileset "nodemap.${nm}.fileset="
-       do_facet mgs $LCTL set_param -P -d nodemap.${nm}.fileset ||
-               error "unable to remove fileset rule on $nm nodemap"
+       if ! $have_persistent_fset_cmd; then
+               do_facet mgs $LCTL set_param -P -d nodemap.${nm}.fileset ||
+                       error "unable to remove fileset rule on $nm nodemap"
+       fi
 
        # re-mount client
        zconf_umount_clients ${clients_arr[0]} $MOUNT ||
index 162d608..52e550f 100644 (file)
@@ -414,7 +414,7 @@ int jt_lcfg_param(int argc, char **argv)
        return jt_lcfg_ioctl(&bufs, argv[0], LCFG_PARAM);
 }
 
-static int lcfg_setparam_perm(const char *func, char *buf)
+static int lcfg_setparam_perm(const char *func, char *buf, bool del)
 {
        int rc = 0;
        struct lustre_cfg_bufs bufs;
@@ -436,8 +436,9 @@ static int lcfg_setparam_perm(const char *func, char *buf)
         * future.
         */
        lustre_cfg_bufs_set_string(&bufs, 0, "general");
-
        lustre_cfg_bufs_set_string(&bufs, 1, buf);
+       if (del)
+               lustre_cfg_bufs_set_string(&bufs, 2, "del");
 
        lcfg = malloc(lustre_cfg_len(bufs.lcfg_bufcount,
                                     bufs.lcfg_buflen));
@@ -482,21 +483,19 @@ int jt_lcfg_setparam_perm(int argc, char **argv, struct param_opts *popt)
                        size_t len;
 
                        len = strlen(buf);
-                       /* Consider param ends at the first '=' in the buffer
-                        * and make sure it always ends with '=' as well
-                        */
+                       /* make sure it always ends with '=' */
                        end_pos = memchr(buf, '=', len - 1);
-                       if (end_pos) {
-                               *(++end_pos) = '\0';
-                       } else if (buf[len - 1] != '=') {
-                               buf = malloc(len + 2);
+                       if (!end_pos) {
+                               size_t buflen = len + 2;
+
+                               buf = malloc(buflen);
                                if (buf == NULL)
                                        return -ENOMEM;
-                               sprintf(buf, "%s=", argv[i]);
+                               snprintf(buf, buflen, "%s=", argv[i]);
                        }
                }
 
-               rc = lcfg_setparam_perm(argv[0], buf);
+               rc = lcfg_setparam_perm(argv[0], buf, popt->po_delete);
                if (buf != argv[i])
                        free(buf);
        }
@@ -504,7 +503,7 @@ int jt_lcfg_setparam_perm(int argc, char **argv, struct param_opts *popt)
        return rc;
 }
 
-static int lcfg_conf_param(const char *func, char *buf)
+static int lcfg_conf_param(const char *func, char *buf, bool del)
 {
        int rc;
        struct lustre_cfg_bufs bufs;
@@ -512,6 +511,8 @@ static int lcfg_conf_param(const char *func, char *buf)
 
        lustre_cfg_bufs_reset(&bufs, NULL);
        lustre_cfg_bufs_set_string(&bufs, 1, buf);
+       if (del)
+               lustre_cfg_bufs_set_string(&bufs, 2, "del");
 
        /* We could put other opcodes here. */
        lcfg = malloc(lustre_cfg_len(bufs.lcfg_bufcount, bufs.lcfg_buflen));
@@ -542,7 +543,7 @@ static int lcfg_conf_param(const char *func, char *buf)
 int jt_lcfg_confparam(int argc, char **argv)
 {
        int rc;
-       int del = 0;
+       bool del = false;
        char *buf = NULL;
 
        /* mgs_setparam processes only lctl buf #1 */
@@ -552,7 +553,7 @@ int jt_lcfg_confparam(int argc, char **argv)
        while ((rc = getopt(argc, argv, "d")) != -1) {
                switch (rc) {
                case 'd':
-                       del = 1;
+                       del = true;
                        break;
                default:
                        return CMD_HELP;
@@ -560,28 +561,28 @@ int jt_lcfg_confparam(int argc, char **argv)
        }
 
        buf = argv[optind];
-
        if (del) {
-               char *ptr;
-
-               /* for delete, make it "<param>=\0" */
-               buf = malloc(strlen(argv[optind]) + 2);
-               if (!buf) {
-                       rc = -ENOMEM;
-                       goto out;
+               char *end_pos;
+               size_t len;
+
+               len = strlen(buf);
+               /* make sure it always ends with '=' */
+               end_pos = memchr(buf, '=', len - 1);
+               if (!end_pos) {
+                       size_t buflen = len + 2;
+
+                       buf = malloc(buflen);
+                       if (buf == NULL)
+                               return -ENOMEM;
+                       snprintf(buf, buflen, "%s=", argv[optind]);
                }
-               /* put an '=' on the end in case it doesn't have one */
-               sprintf(buf, "%s=", argv[optind]);
-               /* then truncate after the first '=' */
-               ptr = strchr(buf, '=');
-               *(++ptr) = '\0';
        }
 
-       rc = lcfg_conf_param(argv[0], buf);
+       rc = lcfg_conf_param(argv[0], buf, del);
 
        if (buf != argv[optind])
                free(buf);
-out:
+
        if (rc < 0) {
                fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
                        strerror(-rc));
@@ -1235,9 +1236,9 @@ static int lcfg_apply_param_yaml(const char *func, const char *filename)
                               "set_param" : "conf_param", buf);
 
                        if (confset == PT_SETPARAM)
-                               rc = lcfg_setparam_perm(func, buf);
+                               rc = lcfg_setparam_perm(func, buf, 0);
                        else
-                               rc = lcfg_conf_param(func, buf);
+                               rc = lcfg_conf_param(func, buf, 0);
                        if (rc) {
                                printf("error: failed to apply parameter rc = %d, tyring next one\n",
                                       rc);