dst->nmf_readonly_mount = 0;
dst->nmf_rbac = NODEMAP_RBAC_ALL;
dst->nmf_deny_mount = 0;
+ dst->nmf_fileset_use_iam = 0;
dst->nm_squash_uid = NODEMAP_NOBODY_UID;
dst->nm_squash_gid = NODEMAP_NOBODY_GID;
dst->nmf_readonly_mount = src->nmf_readonly_mount;
dst->nmf_rbac = src->nmf_rbac;
dst->nmf_deny_mount = src->nmf_deny_mount;
+ dst->nmf_fileset_use_iam = 0;
+
dst->nm_squash_uid = src->nm_squash_uid;
dst->nm_squash_gid = src->nm_squash_gid;
dst->nm_squash_projid = src->nm_squash_projid;
EXPORT_SYMBOL(nodemap_del_range);
/**
- * set fileset on nodemap
- * \param name nodemap to set fileset on
+ * Set a fileset on a nodemap in memory and the nodemap IAM records.
+ * If the nodemap is dynamic, the nodemap IAM update is transparently skipped in
+ * the nodemap_idx_fileset_* functions to update only the in-memory nodemap.
+ *
+ * \param nodemap the nodemap to set fileset on
* \param fileset string containing fileset
* \retval 0 on success
+ * \retval -EINVAL invalid fileset: Does not start with '/'
+ * \retval -ENAMETOOLONG fileset is too long
+ * \retval -EIO undo operation failed during IAM update
+ */
+static int nodemap_set_fileset_iam(struct lu_nodemap *nodemap,
+ const char *fileset)
+{
+ int rc = 0;
+
+ if (strlen(fileset) > PATH_MAX)
+ RETURN(-ENAMETOOLONG);
+
+ if (fileset[0] == '\0' || strcmp(fileset, "clear") == 0) {
+ rc = nodemap_idx_fileset_clear(nodemap);
+ if (rc == 0)
+ nodemap->nm_fileset[0] = '\0';
+ } else if (fileset[0] != '/') {
+ rc = -EINVAL;
+ } else {
+ /*
+ * Only update the index if the fileset is already set.
+ * If it was set by the params llog, it is not set in the IAM.
+ */
+ if (nodemap->nm_fileset[0] != '\0' &&
+ nodemap->nmf_fileset_use_iam) {
+ rc = nodemap_idx_fileset_update(
+ nodemap, nodemap->nm_fileset, fileset);
+ } else {
+ rc = nodemap_idx_fileset_add(nodemap, fileset);
+ }
+ if (rc < 0)
+ GOTO(out, rc);
+
+ memcpy(nodemap->nm_fileset, fileset, strlen(fileset) + 1);
+ }
+
+ if (!nodemap->nm_dyn && !nodemap->nmf_fileset_use_iam) {
+ nodemap->nmf_fileset_use_iam = 1;
+ rc = nodemap_idx_nodemap_update(nodemap);
+ }
+
+out:
+ return rc;
+}
+
+/**
+ * Set a fileset on a nodemap. This is a local operation and not persistent.
*
- * set a fileset on the named nodemap
+ * This function is a remnant from when fileset updates were made through
+ * the params llog, which caused "lctl set_param" to be called on
+ * each server locally. For backward compatibility this functionality is kept.
+ *
+ * \param nodemap the nodemap to set fileset on
+ * \param fileset string containing fileset
+ * \retval 0 on success
+ * \retval -EINVAL invalid fileset: Does not start with '/'
+ * \retval -ENAMETOOLONG fileset is too long
*/
-static int nodemap_set_fileset_helper(struct nodemap_config *config,
- struct lu_nodemap *nodemap,
- const char *fileset)
+static int nodemap_set_fileset_local(struct lu_nodemap *nodemap,
+ const char *fileset)
{
int rc = 0;
* won't clear fileset.
* 'fileset=""' is still kept for compatibility reason.
*/
- if (fileset == NULL)
- rc = -EINVAL;
- else if (fileset[0] == '\0' || strcmp(fileset, "clear") == 0)
+ if (fileset[0] == '\0' || strcmp(fileset, "clear") == 0) {
nodemap->nm_fileset[0] = '\0';
- else if (fileset[0] != '/')
+ } else if (fileset[0] != '/') {
rc = -EINVAL;
- else if (strscpy(nodemap->nm_fileset, fileset,
- sizeof(nodemap->nm_fileset)) < 0)
+
+ } else if (strscpy(nodemap->nm_fileset, fileset,
+ sizeof(nodemap->nm_fileset)) < 0) {
rc = -ENAMETOOLONG;
+ /* function may be called from llog thread, so we CERROR here */
+ CERROR("%s: fileset '%s' is too long: rc = %d\n",
+ nodemap->nm_name, fileset, rc);
+ }
return rc;
}
-int nodemap_set_fileset(const char *name, const char *fileset, bool checkperm)
+/**
+ * Set fileset on a named nodemap
+ *
+ * \param name name of the nodemap to set fileset on
+ * \param fileset string containing fileset
+ * \param checkperm true if permission check is required
+ * \param ioctl_op true if called from ioctl nodemap functions
+ * \retval 0 on success
+ */
+int nodemap_set_fileset(const char *name, const char *fileset, bool checkperm,
+ bool ioctl_op)
{
- struct lu_nodemap *nodemap = NULL;
- int rc = 0;
+ struct lu_nodemap *nodemap = NULL;
+ int rc = 0;
+
+ ENTRY;
+
+ if (name == NULL || name[0] == '\0' || fileset == NULL)
+ RETURN(-EINVAL);
mutex_lock(&active_config_lock);
nodemap = nodemap_lookup(name);
if (IS_ERR(nodemap)) {
mutex_unlock(&active_config_lock);
- GOTO(out, rc = PTR_ERR(nodemap));
+ RETURN(PTR_ERR(nodemap));
}
if (checkperm && !allow_op_on_nm(nodemap))
GOTO(out_unlock, rc = -EPERM);
- rc = nodemap_set_fileset_helper(active_config, nodemap, fileset);
+ /*
+ * Previously filesets were made persistent through the params llog,
+ * which caused local fileset updates on the server nodes. Now, filesets
+ * are made persistent through the nodemap IAM records. Since we need to
+ * be backward-compatible, this function serves as a mechanism to
+ * support filesets saved in the llog as long as no IAM records were
+ * set. "nodemap->nmf_fileset_use_iam" controls the transition between
+ * both backends. Local updates are disabled once IAM records are used.
+ */
+ if (ioctl_op)
+ rc = nodemap_set_fileset_iam(nodemap, fileset);
+ else if (!ioctl_op && !nodemap->nmf_fileset_use_iam)
+ rc = nodemap_set_fileset_local(nodemap, fileset);
+ else
+ rc = -EINVAL;
out_unlock:
mutex_unlock(&active_config_lock);
nodemap_putref(nodemap);
-out:
+
+ EXIT;
return rc;
}
EXPORT_SYMBOL(nodemap_set_fileset);
}
up_write(&active_config->nmc_range_tree_lock);
+ if (nodemap->nm_fileset[0] != '\0') {
+ rc2 = nodemap_idx_fileset_clear(nodemap);
+ if (rc2 < 0)
+ rc = rc2;
+
+ nodemap->nm_fileset[0] = '\0';
+ }
+
if (!nodemap->nm_dyn) {
rc2 = nodemap_idx_nodemap_del(nodemap);
if (rc2 < 0)
rc = -EINVAL;
break;
case LCFG_NODEMAP_SET_FILESET:
- rc = nodemap_set_fileset(nodemap_name, param, true);
+ rc = nodemap_set_fileset(nodemap_name, param, true, true);
break;
case LCFG_NODEMAP_SET_SEPOL:
rc = nodemap_set_sepol(nodemap_name, param, true);
NM_FL_FORBID_ENCRYPT : 0);
nr->ncr.ncr_flags2 =
(nodemap->nmf_readonly_mount ? NM_FL2_READONLY_MOUNT : 0) |
- (nodemap->nmf_deny_mount ? NM_FL2_DENY_MOUNT : 0);
+ (nodemap->nmf_deny_mount ? NM_FL2_DENY_MOUNT : 0) |
+ (nodemap->nmf_fileset_use_iam ? NM_FL2_FILESET_USE_IAM : 0);
nr->ncr.ncr_padding1 = 0;
nr->ncr.ncr_squash_projid = cpu_to_le32(nodemap->nm_squash_projid);
nr->ncr.ncr_squash_uid = cpu_to_le32(nodemap->nm_squash_uid);
nor->nor_limit_projid = cpu_to_le32(nodemap->nm_offset_limit_projid);
}
+static int nodemap_cluster_fileset_rec_init(union nodemap_rec *nr,
+ const char *fileset,
+ unsigned int fragment_id,
+ unsigned int fragment_size)
+{
+ struct nodemap_fileset_rec *nfr = &nr->nfr;
+ unsigned int fset_offset;
+ int rc = 0;
+
+ if (fragment_size > LUSTRE_NODEMAP_FILESET_FRAGMENT_SIZE ||
+ fragment_size > strlen(fileset) + 1) {
+ rc = -ENAMETOOLONG;
+ CERROR("%s: Invalid fileset fragment size: rc = %d\n", fileset,
+ rc);
+ RETURN(rc);
+ }
+
+ nfr->nfr_fragment_id = cpu_to_le16(fragment_id);
+ fset_offset = fragment_id * LUSTRE_NODEMAP_FILESET_FRAGMENT_SIZE;
+ memcpy(nfr->nfr_path_fragment, fileset + fset_offset, fragment_size);
+
+ return rc;
+}
+
static void nodemap_idmap_key_init(struct nodemap_key *nk, unsigned int nm_id,
enum nodemap_id_type id_type,
u32 id_client)
return nm_obj;
}
-static int nodemap_idx_insert(const struct lu_env *env,
- struct dt_object *idx,
- const struct nodemap_key *nk,
- const union nodemap_rec *nr)
+/**
+ * Batch inserts a number of keys and records into the nodemap IAM.
+ *
+ * \param env execution environment
+ * \param idx index object to insert into
+ * \param nks array of keys to insert
+ * \param nrs array of records to insert
+ * \param count number of keys and records to insert
+ * \param inserted_out pointer to the number of records inserted.
+ * May be set to NULL if not needed.
+ *
+ * \retval negative errno if the insertion fails
+ */
+static int nodemap_idx_insert_batch(const struct lu_env *env,
+ struct dt_object *idx,
+ const struct nodemap_key *nks,
+ const union nodemap_rec *nrs, int count,
+ int *inserted_out)
{
struct thandle *th;
struct dt_device *dev = lu2dt_dev(idx->do_lu.lo_dev);
- int rc;
+ int inserted = 0;
+ int rc, i;
BUILD_BUG_ON(sizeof(union nodemap_rec) != 32);
if (IS_ERR(th))
RETURN(PTR_ERR(th));
- rc = dt_declare_insert(env, idx,
- (const struct dt_rec *)nr,
- (const struct dt_key *)nk, th);
- if (rc != 0)
- GOTO(out, rc);
+ for (i = 0; i < count; i++) {
+ rc = dt_declare_insert(env, idx, (const struct dt_rec *)&nrs[i],
+ (const struct dt_key *)&nks[i], th);
+ if (rc != 0)
+ GOTO(out, rc);
+ }
rc = dt_declare_version_set(env, idx, th);
if (rc != 0)
dt_write_lock(env, idx, 0);
- rc = dt_insert(env, idx, (const struct dt_rec *)nr,
- (const struct dt_key *)nk, th);
+ for (i = 0; i < count; i++) {
+ rc = dt_insert(env, idx, (const struct dt_rec *)&nrs[i],
+ (const struct dt_key *)&nks[i], th);
+ if (rc != 0)
+ break;
+ inserted++;
+ }
nodemap_inc_version(env, idx, th);
dt_write_unlock(env, idx);
out:
dt_trans_stop(env, dev, th);
+ if (inserted_out != NULL)
+ *inserted_out = inserted;
+
return rc;
}
+static int nodemap_idx_insert(const struct lu_env *env,
+ struct dt_object *idx,
+ const struct nodemap_key *nk,
+ const union nodemap_rec *nr)
+{
+ return nodemap_idx_insert_batch(env, idx, nk, nr, 1, NULL);
+}
+
static int nodemap_idx_update(const struct lu_env *env,
struct dt_object *idx,
const struct nodemap_key *nk,
const union nodemap_rec *nr)
{
- struct thandle *th;
- struct dt_device *dev = lu2dt_dev(idx->do_lu.lo_dev);
- int rc = 0;
+ struct thandle *th;
+ struct dt_device *dev = lu2dt_dev(idx->do_lu.lo_dev);
+ int rc = 0;
th = dt_trans_create(env, dev);
if (IS_ERR(th))
return rc;
}
-static int nodemap_idx_delete(const struct lu_env *env,
- struct dt_object *idx,
- const struct nodemap_key *nk,
- const union nodemap_rec *unused)
+/**
+ * Batch deletes a number of keys and records from the nodemap IAM.
+ *
+ * \param env execution environment
+ * \param idx index object to delete
+ * \param nks array of keys to delete
+ * \param nrs array of records to delete
+ * \param count number of keys to delete
+ * \param deleted_out pointer to the number of records deleted.
+ * May be set to NULL if not needed.
+ *
+ * \retval negative errno if the insertion fails.
+ * ENOENT if the key does not exist is ignored.
+ */
+static int nodemap_idx_delete_batch(const struct lu_env *env,
+ struct dt_object *idx,
+ const struct nodemap_key *nks, int count,
+ int *deleted_out)
{
- struct thandle *th;
- struct dt_device *dev = lu2dt_dev(idx->do_lu.lo_dev);
- int rc = 0;
+ struct thandle *th;
+ struct dt_device *dev = lu2dt_dev(idx->do_lu.lo_dev);
+ int deleted = 0;
+ int rc, i;
th = dt_trans_create(env, dev);
if (IS_ERR(th))
RETURN(PTR_ERR(th));
- rc = dt_declare_delete(env, idx, (const struct dt_key *)nk, th);
- if (rc != 0)
- GOTO(out, rc);
+ for (i = 0; i < count; i++) {
+ rc = dt_declare_delete(env, idx, (const struct dt_key *)&nks[i],
+ th);
+ if (rc != 0)
+ GOTO(out, rc);
+ }
rc = dt_declare_version_set(env, idx, th);
if (rc != 0)
dt_write_lock(env, idx, 0);
- rc = dt_delete(env, idx, (const struct dt_key *)nk, th);
+ for (i = 0; i < count; i++) {
+ rc = dt_delete(env, idx, (const struct dt_key *)&nks[i], th);
+ if (rc == -ENOENT)
+ continue;
+ if (rc != 0)
+ break;
- nodemap_inc_version(env, idx, th);
+ deleted++;
+ }
+ nodemap_inc_version(env, idx, th);
dt_write_unlock(env, idx);
out:
dt_trans_stop(env, dev, th);
+ if (deleted_out != NULL)
+ *deleted_out = deleted;
+
return rc;
}
+static int nodemap_idx_delete(const struct lu_env *env,
+ struct dt_object *idx,
+ const struct nodemap_key *nk,
+ const union nodemap_rec *unused)
+{
+ return nodemap_idx_delete_batch(env, idx, nk, 1, NULL);
+}
+
enum nm_add_update {
NM_ADD = 0,
NM_UPDATE = 1,
RETURN(rc);
}
+/**
+ * Inserts fileset fragments (identified by struct lu_nodemap_fileset_info) into
+ * the nodemap IAM.
+ *
+ * \param fset_info fileset info to be inserted into the nodemap IAM
+ * \param env execution environment
+ * \param idx index object to insert into
+ * \param inserted_out pointer to the number of records inserted.
+ * May be set to NULL if not needed.
+ *
+ * \retval 0 on success
+ * \retval -EINVAL invalid input parameters
+ * \retval -ENOMEM memory allocation failure
+ */
+static int nodemap_idx_fileset_fragments_add(
+ const struct lu_nodemap_fileset_info *fset_info,
+ const struct lu_env *env, struct dt_object *idx, int *inserted_out)
+{
+ struct nodemap_key *nk_array;
+ union nodemap_rec *nr_array;
+ unsigned int size_remaining, fragment_size;
+ int i;
+ int rc = 0;
+
+ if (fset_info == NULL || fset_info->nfi_fileset == NULL ||
+ fset_info->nfi_fragment_cnt == 0 ||
+ fset_info->nfi_fragment_cnt >
+ LUSTRE_NODEMAP_FILESET_SUBID_RANGE - 1)
+ RETURN(-EINVAL);
+
+ OBD_ALLOC_PTR_ARRAY(nk_array, fset_info->nfi_fragment_cnt);
+ if (nk_array == NULL)
+ RETURN(-ENOMEM);
+
+ OBD_ALLOC_PTR_ARRAY(nr_array, fset_info->nfi_fragment_cnt);
+ if (nr_array == NULL)
+ GOTO(out_cleanup, rc = -ENOMEM);
+
+ /* setup fileset fragment keys and records to be inserted */
+ size_remaining = (unsigned int) strlen(fset_info->nfi_fileset) + 1;
+ fragment_size = LUSTRE_NODEMAP_FILESET_FRAGMENT_SIZE;
+ for (i = 0; i < fset_info->nfi_fragment_cnt; i++) {
+ if (size_remaining < LUSTRE_NODEMAP_FILESET_FRAGMENT_SIZE)
+ fragment_size = size_remaining;
+
+ rc = nodemap_cluster_fileset_rec_init(
+ &nr_array[i], fset_info->nfi_fileset, i, fragment_size);
+ if (rc != 0)
+ GOTO(out_cleanup, rc);
+
+ nodemap_cluster_key_init(&nk_array[i], fset_info->nfi_nm_id,
+ fset_info->nfi_subid + i);
+ size_remaining -= fragment_size;
+ }
+ rc = nodemap_idx_insert_batch(env, idx, nk_array, nr_array,
+ fset_info->nfi_fragment_cnt,
+ inserted_out);
+
+out_cleanup:
+ OBD_FREE_PTR_ARRAY(nr_array, fset_info->nfi_fragment_cnt);
+ OBD_FREE_PTR_ARRAY(nk_array, fset_info->nfi_fragment_cnt);
+
+ return rc;
+}
+
+/**
+ * Deletes fileset fragments (identified by struct lu_nodemap_fileset_info) from
+ * the nodemap IAM.
+ *
+ * \param fset_info fileset info to be deleted from the nodemap IAM
+ * \param env execution environment
+ * \param idx index object to delete from
+ * \param deleted_out pointer to the number of records deleted.
+ * May be set to NULL if not needed.
+ *
+ * \retval 0 on success
+ * \retval -EINVAL invalid input parameters
+ * \retval -ENOMEM memory allocation failure
+ */
+static int nodemap_idx_fileset_fragments_del(
+ const struct lu_nodemap_fileset_info *fset_info,
+ const struct lu_env *env, struct dt_object *idx, int *deleted_out)
+{
+ struct nodemap_key *nk_array;
+ unsigned int i;
+ int rc = 0;
+
+ if (fset_info == NULL || fset_info->nfi_fragment_cnt == 0 ||
+ fset_info->nfi_fragment_cnt >
+ LUSTRE_NODEMAP_FILESET_SUBID_RANGE - 1)
+ RETURN(-EINVAL);
+
+ OBD_ALLOC_PTR_ARRAY(nk_array, fset_info->nfi_fragment_cnt);
+ if (nk_array == NULL)
+ RETURN(-ENOMEM);
+
+ /* setup fileset fragment keys to be deleted */
+ for (i = 0; i < fset_info->nfi_fragment_cnt; i++) {
+ nodemap_cluster_key_init(&nk_array[i], fset_info->nfi_nm_id,
+ fset_info->nfi_subid + i);
+ }
+
+ rc = nodemap_idx_delete_batch(env, idx, nk_array,
+ fset_info->nfi_fragment_cnt, deleted_out);
+
+ OBD_FREE_PTR_ARRAY(nk_array, fset_info->nfi_fragment_cnt);
+
+ return rc;
+}
+
+/**
+ * Clears the full fileset sub id range from the nodemap IAM.
+ *
+ * \param nodemap nodemap where the fileset is set
+ * \param env execution environment
+ * \param idx index object to delete from
+ *
+ * \retval 0 on success (ENOENT during idx_delete is ignored)
+ * \retval -ENOMEM memory allocation failure
+ */
+static int nodemap_idx_fileset_fragments_clear(const struct lu_nodemap *nodemap,
+ const struct lu_env *env,
+ struct dt_object *idx)
+{
+ struct nodemap_key *nk_array;
+ unsigned int count, subid, i;
+ int rc = 0;
+
+ count = LUSTRE_NODEMAP_FILESET_SUBID_RANGE;
+
+ OBD_ALLOC_PTR_ARRAY(nk_array, count);
+ if (nk_array == NULL)
+ RETURN(-ENOMEM);
+
+ /* setup fileset fragment keys to be deleted */
+ for (i = 0; i < count; i++) {
+ subid = NODEMAP_FILESET + i;
+ nodemap_cluster_key_init(&nk_array[i], nodemap->nm_id, subid);
+ }
+
+ rc = nodemap_idx_delete_batch(env, idx, nk_array, count, NULL);
+ if (rc == -ENOENT)
+ rc = 0;
+
+ OBD_FREE_PTR_ARRAY(nk_array, count);
+
+ return rc;
+}
+
+/**
+ * Initializes the fileset info structure based on nodemap and fileset info.
+ *
+ * \param fset_info fileset info structure to be initialized
+ * \param fileset fileset name
+ * \param nodemap nodemap where the fileset is set
+ * \param subid starting subid of the fileset in the IAM
+ */
+static void nodemap_fileset_info_init(struct lu_nodemap_fileset_info *fset_info,
+ const char *fileset,
+ const struct lu_nodemap *nodemap,
+ enum nodemap_cluster_rec_subid subid)
+{
+ unsigned int fset_size;
+
+ fset_info->nfi_nm_id = nodemap->nm_id;
+ fset_info->nfi_subid = subid;
+ fset_info->nfi_fileset = fileset;
+
+ fset_size = (unsigned int)strlen(fset_info->nfi_fileset) + 1;
+ fset_info->nfi_fragment_cnt =
+ fset_size / LUSTRE_NODEMAP_FILESET_FRAGMENT_SIZE;
+
+ if (fset_size % LUSTRE_NODEMAP_FILESET_FRAGMENT_SIZE > 0)
+ fset_info->nfi_fragment_cnt++;
+}
+
+/**
+ * Adds a fileset to the nodemap IAM.
+ *
+ * If an error occurs during the IAM insert operation, the already inserted
+ * fragments are deleted. In case the latter undo operation fails, the fileset
+ * is subid range is cleared and -EIO is returned.
+ *
+ * \param nodemap the nodemap to insert the fileset
+ * \param fileset fileset name to insert
+ *
+ * \retval 0 on success
+ * \retval -EINVAL invalid input parameters
+ * \retval -EIO undo operation failed
+ */
+int nodemap_idx_fileset_add(const struct lu_nodemap *nodemap,
+ const char *fileset)
+{
+ struct lu_nodemap_fileset_info fset_info;
+ struct lu_env env;
+ struct dt_object *idx;
+ int inserted, deleted, rc2;
+ int rc = 0;
+
+ ENTRY;
+
+ if (!nodemap_mgs()) {
+ if (nodemap->nm_dyn)
+ return 0;
+
+ rc = -EINVAL;
+ CERROR("%s: cannot add nodemap config to non-existing MGS: rc = %d\n",
+ nodemap->nm_name, rc);
+ RETURN(rc);
+ }
+
+ rc = lu_env_init(&env, LCT_LOCAL);
+ if (rc)
+ RETURN(rc);
+
+ idx = nodemap_mgs_ncf->ncf_obj;
+
+ /*
+ * NODEMAP_FILESET + 1 is static for a single fileset.
+ * For multiple filesets, this will be dynamically computed
+ * based on an incoming fileset ID.
+ */
+ nodemap_fileset_info_init(&fset_info, fileset, nodemap,
+ NODEMAP_FILESET + 1);
+
+ inserted = 0;
+ rc = nodemap_idx_fileset_fragments_add(&fset_info, &env, idx,
+ &inserted);
+
+ if (rc < 0 && inserted != fset_info.nfi_fragment_cnt) {
+ if (inserted == 0)
+ GOTO(out, rc);
+ /* Only some fileset fragments were added, attempt undo */
+ fset_info.nfi_fragment_cnt = inserted;
+ deleted = 0;
+
+ rc2 = nodemap_idx_fileset_fragments_del(&fset_info, &env, idx,
+ &deleted);
+ if (rc2 < 0 && deleted != fset_info.nfi_fragment_cnt) {
+ CERROR("%s: Undo adding fileset failed. rc = %d : rc2 = %d\n",
+ fset_info.nfi_fileset, rc, rc2);
+ /* undo failed. wipe the fileset and set error code */
+ rc2 = nodemap_idx_fileset_fragments_clear(nodemap, &env,
+ idx);
+ rc = -EIO;
+ }
+ }
+
+out:
+ lu_env_fini(&env);
+ return rc;
+}
+
+/**
+ * Updates an existing fileset on the nodemap IAM.
+ *
+ * If an error occurs during the IAM operation, an undo operation is performed.
+ * In case the undo operation fails, the fileset is subid range is cleared
+ * and -EIO is returned.
+ *
+ * \param nodemap the nodemap to update the fileset
+ * \param fileset_old fileset name to be deleted
+ * \param fileset_new fileset name to be inserted
+ *
+ * \retval 0 on success
+ * \retval -EINVAL invalid input parameters
+ * \retval -EIO undo operation failed
+ */
+int nodemap_idx_fileset_update(const struct lu_nodemap *nodemap,
+ const char *fileset_old, const char *fileset_new)
+{
+ struct lu_env env;
+ int rc = 0;
+
+ ENTRY;
+
+ if (!nodemap_mgs()) {
+ if (nodemap->nm_dyn)
+ return 0;
+
+ rc = -EINVAL;
+ CERROR("%s: cannot add nodemap config to non-existing MGS: rc = %d\n",
+ nodemap->nm_name, rc);
+ RETURN(rc);
+ }
+
+ rc = lu_env_init(&env, LCT_LOCAL);
+ if (rc != 0)
+ RETURN(rc);
+
+ rc = nodemap_idx_fileset_del(nodemap, fileset_old);
+ if (rc < 0)
+ GOTO(out, rc);
+
+ rc = nodemap_idx_fileset_add(nodemap, fileset_new);
+
+out:
+ lu_env_fini(&env);
+ return rc;
+}
+
+/**
+ * Deletes a fileset from the nodemap IAM.
+ *
+ * If an error occurs during the IAM deleted operation, the already deleted
+ * fragments are re-inserted. In case the latter undo operation fails,
+ * the fileset is subid range is cleared and -EIO is returned.
+ *
+ * \param nodemap the nodemap to delete from
+ * \param fileset fileset name to be deleted
+ *
+ * \retval 0 on success
+ * \retval -EINVAL invalid input parameters
+ * \retval -EIO undo operation failed
+ */
+int nodemap_idx_fileset_del(const struct lu_nodemap *nodemap,
+ const char *fileset)
+{
+ struct lu_env env;
+ struct dt_object *idx;
+ struct lu_nodemap_fileset_info fset_info;
+ int deleted, inserted, rc2;
+ int rc = 0;
+
+ ENTRY;
+
+ if (!nodemap_mgs()) {
+ if (nodemap->nm_dyn)
+ return 0;
+
+ rc = -EINVAL;
+ CERROR("%s: cannot add nodemap config to non-existing MGS: rc = %d\n",
+ nodemap->nm_name, rc);
+ RETURN(rc);
+ }
+
+ rc = lu_env_init(&env, LCT_LOCAL);
+ if (rc != 0)
+ RETURN(rc);
+
+ idx = nodemap_mgs_ncf->ncf_obj;
+
+ nodemap_fileset_info_init(&fset_info, fileset, nodemap,
+ NODEMAP_FILESET + 1);
+
+ deleted = 0;
+ rc = nodemap_idx_fileset_fragments_del(
+ &fset_info, &env, nodemap_mgs_ncf->ncf_obj, &deleted);
+ if (rc < 0 && deleted != fset_info.nfi_fragment_cnt) {
+ if (deleted == 0)
+ GOTO(out, rc);
+ /*
+ * Only some fileset fragments were deleted,
+ * attempt undo based on the initial fileset, set in fset_info
+ */
+ fset_info.nfi_fragment_cnt = deleted;
+ rc2 = nodemap_idx_fileset_fragments_add(&fset_info, &env, idx,
+ &inserted);
+ if (rc2 < 0 && inserted != fset_info.nfi_fragment_cnt) {
+ CERROR("%s: Undo deleting fileset failed. rc = %d : rc2 = %d\n",
+ fset_info.nfi_fileset, rc, rc2);
+ /* undo failed. wipe the fileset and set error code */
+ rc2 = nodemap_idx_fileset_fragments_clear(nodemap, &env,
+ idx);
+ rc = -EIO;
+ }
+ }
+
+out:
+ lu_env_fini(&env);
+ return rc;
+}
+
+/**
+ * Clears a fileset subid range from the nodemap IAM.
+ *
+ *
+ * \param nodemap nodemap where the fileset is set to be cleared
+ *
+ * \retval 0 on success (ENOENT during idx_delete is ignored)
+ * \retval -EINVAL invalid input parameters
+ */
+int nodemap_idx_fileset_clear(const struct lu_nodemap *nodemap)
+{
+ struct lu_env env;
+ struct dt_object *idx;
+ int rc = 0;
+
+ ENTRY;
+
+ if (!nodemap_mgs()) {
+ if (nodemap->nm_dyn)
+ return 0;
+
+ rc = -EINVAL;
+ CERROR("%s: cannot add nodemap config to non-existing MGS: rc = %d\n",
+ nodemap->nm_name, rc);
+ RETURN(rc);
+ }
+
+ rc = lu_env_init(&env, LCT_LOCAL);
+ if (rc != 0)
+ RETURN(rc);
+
+ idx = nodemap_mgs_ncf->ncf_obj;
+
+ rc = nodemap_idx_fileset_fragments_clear(nodemap, &env, idx);
+
+ lu_env_fini(&env);
+ return rc;
+}
+
int nodemap_idx_range_add(const struct lu_nid_range *range)
{
struct nodemap_key nk;
flags2 = rec->ncr.ncr_flags2;
nodemap->nmf_readonly_mount = flags2 & NM_FL2_READONLY_MOUNT;
nodemap->nmf_deny_mount = flags2 & NM_FL2_DENY_MOUNT;
+ nodemap->nmf_fileset_use_iam = flags2 & NM_FL2_FILESET_USE_IAM;
+
/* by default, and in the absence of cluster_roles, grant all roles */
nodemap->nmf_rbac = NODEMAP_RBAC_ALL;
- /* The fileset should be saved otherwise it will be empty
- * every time in case of "NODEMAP_CLUSTER_IDX".
+ /*
+ * If the use IAM flag has not been set on the nodemap, a llog-based
+ * fileset may be in use on the old nodemap. It needs to be separately
+ * copied to the new nodemap as it is otherwise lost when the IAM
+ * is read for type "NODEMAP_CLUSTER_IDX".
*/
- mutex_lock(&active_config_lock);
- old_nm = nodemap_lookup(rec->ncr.ncr_name);
- if (!IS_ERR(old_nm) && old_nm->nm_fileset[0] != '\0')
- strscpy(nodemap->nm_fileset, old_nm->nm_fileset,
- sizeof(nodemap->nm_fileset));
- mutex_unlock(&active_config_lock);
- if (!IS_ERR(old_nm))
- nodemap_putref(old_nm);
+ if (!nodemap->nmf_fileset_use_iam) {
+ mutex_lock(&active_config_lock);
+ old_nm = nodemap_lookup(rec->ncr.ncr_name);
+ if (!IS_ERR(old_nm) && old_nm->nm_fileset[0] != '\0')
+ strscpy(nodemap->nm_fileset, old_nm->nm_fileset,
+ sizeof(nodemap->nm_fileset));
+ mutex_unlock(&active_config_lock);
+ if (!IS_ERR(old_nm))
+ nodemap_putref(old_nm);
+ }
if (*recent_nodemap == NULL) {
*recent_nodemap = nodemap;
}
/**
+ * Process a fileset fragment and apply it to the current nodemap. The incoming
+ * path fragment is copied to the nodemap fileset based on the fragment ID
+ * which is used to compute the char* offset.
+ *
+ * Fragments processed by this function do not need to be in order.
+ *
+ * \param nodemap nodemap to update with this fileset fragment
+ * \param rec fileset fragment record
+ */
+static int nodemap_cluster_fileset_helper(struct lu_nodemap *nodemap,
+ const union nodemap_rec *rec)
+{
+ unsigned int fragment_id, fragment_len, fset_offset, fset_len_remain;
+
+ fragment_id = le16_to_cpu(rec->nfr.nfr_fragment_id);
+ fragment_len = LUSTRE_NODEMAP_FILESET_FRAGMENT_SIZE;
+
+ /* compute nodemap fileset position */
+ fset_offset = fragment_id * LUSTRE_NODEMAP_FILESET_FRAGMENT_SIZE;
+ fset_len_remain = sizeof(nodemap->nm_fileset) - fset_offset;
+
+ if (fragment_len > fset_len_remain)
+ fragment_len = fset_len_remain;
+
+ memcpy(nodemap->nm_fileset + fset_offset, rec->nfr.nfr_path_fragment,
+ fragment_len);
+
+ return 0;
+}
+
+/**
* Process a key/rec pair and modify the new configuration.
*
* \param config configuration to update with this key/rec data
enum nodemap_idx_type type;
enum nodemap_id_type id_type;
struct lnet_nid nid[2];
- int subtype;
+ int subtype, cluster_idx_key;
u32 nodemap_id;
u32 map[2];
- int rc;
+ int rc = 0;
ENTRY;
GOTO(out, rc = -ENOENT);
}
- /* update most recently used nodemap if necessay */
+ /* update most recently used nodemap if necessary */
if (nodemap != *recent_nodemap)
*recent_nodemap = nodemap;
}
nodemap->nm_name, nodemap_id);
break;
case NODEMAP_CLUSTER_IDX:
- switch (nodemap_get_key_subtype(key)) {
- case NODEMAP_CLUSTER_REC:
+ {
+ cluster_idx_key = nodemap_get_key_subtype(key);
+ if (cluster_idx_key == NODEMAP_CLUSTER_REC) {
rc = nodemap_cluster_rec_helper(config, nodemap_id, rec,
recent_nodemap);
- if (rc != 0)
- GOTO(out, rc);
- break;
- case NODEMAP_CLUSTER_ROLES:
+ } else if (cluster_idx_key == NODEMAP_CLUSTER_ROLES) {
rc = nodemap_cluster_roles_helper(nodemap, rec);
- if (rc != 0)
- GOTO(out, rc);
- break;
- case NODEMAP_CLUSTER_OFFSET:
+ } else if (cluster_idx_key == NODEMAP_CLUSTER_OFFSET) {
/* only works for offset UID = GID = PROJID */
- rc = nodemap_add_offset_helper(nodemap,
- le32_to_cpu(rec->nor.nor_start_uid),
- le32_to_cpu(rec->nor.nor_limit_uid));
- if (rc != 0)
- GOTO(out, rc);
- break;
- default:
+ rc = nodemap_add_offset_helper(
+ nodemap, le32_to_cpu(rec->nor.nor_start_uid),
+ le32_to_cpu(rec->nor.nor_limit_uid));
+ } else if (cluster_idx_key >= NODEMAP_FILESET &&
+ cluster_idx_key <
+ NODEMAP_FILESET +
+ LUSTRE_NODEMAP_FILESET_SUBID_RANGE) {
+ rc = nodemap_cluster_fileset_helper(nodemap, rec);
+ } else {
CWARN("%s: ignoring keyrec of type %d with subtype %u\n",
nodemap->nm_name, NODEMAP_CLUSTER_IDX,
nodemap_get_key_subtype(key));
- break;
}
+ if (rc != 0)
+ GOTO(out, rc);
break;
+ }
case NODEMAP_RANGE_IDX:
lnet_nid4_to_nid(le64_to_cpu(rec->nrr.nrr_start_nid), &nid[0]);
lnet_nid4_to_nid(le64_to_cpu(rec->nrr.nrr_end_nid), &nid[1]);
}
run_test 26 "test transferring very large nodemap"
+nodemap_exercise_fileset_cleanup() {
+ # Already mounted clients are skipped in zconf_mount_clients()
+ for client in "${clients_arr[@]}"; do
+ zconf_mount_clients $client $MOUNT $MOUNT_OPTS ||
+ error "unable to mount client $client"
+ done
+}
+
nodemap_exercise_fileset() {
- local nm="$1"
- local loop=0
+ local have_persistent_fset_cmd
local check_proj=true
+ local loop=0
+ local nm="$1"
(( $MDS1_VERSION >= $(version_code 2.14.52) )) || check_proj=false
+ # when "have_persistent_fset_cmd" is true, "lctl nodemap_set_fileset"
+ # is persistent, otherwise "lctl set_param -P" must be used
+ have_persistent_fset_cmd=false
+ (( $MGS_VERSION >= $(version_code 2.16.51) )) &&
+ have_persistent_fset_cmd=true
+
# setup
- if [ "$nm" == "default" ]; then
+ if [[ "$nm" == "default" ]]; then
do_facet mgs $LCTL nodemap_activate 1
wait_nm_sync active
do_facet mgs $LCTL nodemap_modify --name default \
--property admin --value 1
do_facet mgs $LCTL nodemap_modify --name default \
--property trusted --value 1
- wait_nm_sync default admin_nodemap
wait_nm_sync default trusted_nodemap
check_proj=false
else
fileset_test_setup "$nm"
# add fileset info to $nm nodemap
- if ! combined_mgs_mds; then
- do_facet mgs $LCTL set_param nodemap.${nm}.fileset=/$subdir ||
- error "unable to add fileset info to $nm nodemap on MGS"
+ if $have_persistent_fset_cmd; then
+ do_facet mgs $LCTL nodemap_set_fileset --name $nm \
+ --fileset "/${subdir}" ||
+ error "can't set fileset to $nm nodemap on MGS"
+ # check fileset is set on local mgs node
+ wait_update_facet mgs "$LCTL get_param nodemap.${nm}.fileset" \
+ "nodemap.${nm}.fileset=/${subdir}" ||
+ error "fileset /${subdir} not set on $nm nodemap"
+ else
+ if ! combined_mgs_mds; then
+ do_facet mgs $LCTL set_param \
+ nodemap.${nm}.fileset=/${subdir} ||
+ error "can't set fileset /${subdir} to $nm nodemap on MGS"
+ fi
+ do_facet mgs $LCTL set_param -P \
+ nodemap.${nm}.fileset=/${subdir} ||
+ error "can't set fileset /${subdir} to $nm nodemap on servers"
fi
- do_facet mgs $LCTL set_param -P nodemap.${nm}.fileset=/$subdir ||
- error "unable to add fileset info to $nm nodemap for servers"
- wait_nm_sync $nm fileset "nodemap.${nm}.fileset=/$subdir"
+
+ # check fileset is set on remote nodes
+ wait_nm_sync $nm fileset "nodemap.${nm}.fileset=/${subdir}"
if $check_proj; then
do_facet mgs $LCTL nodemap_modify --name $nm \
wait_nm_sync $nm deny_unknown
fi
- # re-mount client
- zconf_umount_clients ${clients_arr[0]} $MOUNT ||
- error "unable to umount client ${clients_arr[0]}"
+ # re-start all components to verify persistence of fileset after restart
+ stopall || error "unable to stop"
+ # Unload modules to fully reload nodemap IAM
+ LOAD_MODULES_REMOTE=true unload_modules ||
+ error "unable to unload modules"
+ LOAD_MODULES_REMOTE=true load_modules ||
+ error "unable to load modules"
+ mountmgs || error "unable to start mgs"
+ mountmds || error "unable to start mds"
+ mountoss || error "unable to start oss"
+
+ stack_trap nodemap_exercise_fileset_cleanup EXIT
+
+ # mount a single client for fileset testing and remount
+ # the remaining clients later.
# set some generic fileset to trigger SSK code
export FILESET=/
zconf_mount_clients ${clients_arr[0]} $MOUNT $MOUNT_OPTS ||
# remove fileset info from nodemap
do_facet mgs $LCTL nodemap_set_fileset --name $nm --fileset clear ||
error "unable to delete fileset info on $nm nodemap"
+ # check whether fileset was removed on mgs
wait_update_facet mgs "$LCTL get_param nodemap.${nm}.fileset" \
"nodemap.${nm}.fileset=" ||
error "fileset info still not cleared on $nm nodemap"
- do_facet mgs $LCTL set_param -P nodemap.${nm}.fileset=clear ||
+ if ! $have_persistent_fset_cmd; then
+ do_facet mgs $LCTL set_param -P nodemap.${nm}.fileset=clear ||
error "unable to reset fileset info on $nm nodemap"
+ fi
+
+ # 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"
error "unable to umount client ${clients_arr[0]}"
fi
fileset_test_cleanup "$nm"
- if [ "$nm" == "default" ]; then
+ if [[ "$nm" == "default" ]]; then
do_facet mgs $LCTL nodemap_modify --name default \
--property admin --value 0
do_facet mgs $LCTL nodemap_modify --name default \
--property trusted --value 0
- wait_nm_sync default admin_nodemap
wait_nm_sync default trusted_nodemap
do_facet mgs $LCTL nodemap_activate 0
wait_nm_sync active 0
else
nodemap_test_cleanup
fi
- if $SHARED_KEY; then
- zconf_mount_clients ${clients_arr[0]} $MOUNT $MOUNT_OPTS ||
- error "unable to remount client ${clients_arr[0]}"
- fi
+ # The fileset cleanup trap is reset during nodemap clean up.
+ # Call fileset cleanup to restart all shut down clients
+ nodemap_exercise_fileset_cleanup
}
test_27a() {
- [ "$MDS1_VERSION" -lt $(version_code 2.11.50) ] &&
+ (( $MDS1_VERSION < $(version_code 2.11.50) )) &&
skip "Need MDS >= 2.11.50"
# if servers run on the same node, it is impossible to tell if they get
# synced with the mgs, so this test needs to be skipped
- if [ $(facet_active_host mgs) == $(facet_active_host mds) ] &&
- [ $(facet_active_host mgs) == $(facet_active_host ost1) ]; then
+ if [[ $(facet_active_host mgs) == $(facet_active_host mds) ]] &&
+ [[ $(facet_active_host mgs) == $(facet_active_host ost1) ]]; then
skip "local mode not supported"
fi
for nm in "default" "c0"; do
- local subdir="subdir_${nm}"
+ local subdir="thisisaverylongsubdirtotestlongfilesetsandtotestmultiplefilesetfragmentsonthenodemapiam_${nm}"
local subsubdir="subsubdir_${nm}"
- if [ "$nm" == "default" ] && [ "$SHARED_KEY" == "true" ]; then
- echo "Skipping nodemap $nm with SHARED_KEY";
- continue;
+ if [[ "$nm" == "default" && "$SHARED_KEY" == "true" ]]; then
+ echo "Skipping nodemap $nm with SHARED_KEY"
+ continue
fi
echo "Exercising fileset for nodemap $nm"