}
/* Rule based auto caching */
-static void pcc_id_list_free(struct list_head *id_list)
+static void pcc_id_list_free(struct pcc_expression *expr)
{
struct pcc_match_id *id, *n;
- list_for_each_entry_safe(id, n, id_list, pmi_linkage) {
- list_del_init(&id->pmi_linkage);
- OBD_FREE_PTR(id);
+ if (expr->pe_opc == PCC_FIELD_OP_EQ) {
+ list_for_each_entry_safe(id, n, &expr->pe_cond, pmi_linkage) {
+ list_del_init(&id->pmi_linkage);
+ OBD_FREE_PTR(id);
+ }
}
}
-static void pcc_fname_list_free(struct list_head *fname_list)
+static void pcc_fname_list_free(struct pcc_expression *expr)
{
struct pcc_match_fname *fname, *n;
- list_for_each_entry_safe(fname, n, fname_list, pmf_linkage) {
+ LASSERT(expr->pe_opc == PCC_FIELD_OP_EQ);
+ list_for_each_entry_safe(fname, n, &expr->pe_cond, pmf_linkage) {
OBD_FREE(fname->pmf_name, strlen(fname->pmf_name) + 1);
list_del_init(&fname->pmf_linkage);
OBD_FREE_PTR(fname);
}
}
+static void pcc_size_list_free(struct pcc_expression *expr)
+{
+ struct pcc_match_size *sz, *n;
+
+ if (expr->pe_opc == PCC_FIELD_OP_EQ) {
+ list_for_each_entry_safe(sz, n, &expr->pe_cond, pms_linkage) {
+ list_del_init(&sz->pms_linkage);
+ OBD_FREE_PTR(sz);
+ }
+ }
+}
+
static void pcc_expression_free(struct pcc_expression *expr)
{
LASSERT(expr->pe_field >= PCC_FIELD_UID &&
case PCC_FIELD_UID:
case PCC_FIELD_GID:
case PCC_FIELD_PROJID:
- pcc_id_list_free(&expr->pe_cond);
+ pcc_id_list_free(expr);
break;
case PCC_FIELD_FNAME:
- pcc_fname_list_free(&expr->pe_cond);
+ pcc_fname_list_free(expr);
+ break;
+ case PCC_FIELD_SIZE:
+ pcc_size_list_free(expr);
break;
default:
LBUG();
#define PCC_DISJUNCTION_DELIM (',')
#define PCC_CONJUNCTION_DELIM ('&')
-#define PCC_EXPRESSION_DELIM ('=')
+#define PCC_EXPRESSION_DELIM_EQ ('=')
+#define PCC_EXPRESSION_DELIM_LT ('<')
+#define PCC_EXPRESSION_DELIM_GT ('>')
static int
pcc_fname_list_add(struct cfs_lstr *id, struct list_head *fname_list)
}
static int
-pcc_fname_list_parse(char *str, int len, struct list_head *fname_list)
+pcc_fname_list_parse(char *str, int len, struct pcc_expression *expr)
{
struct cfs_lstr src;
struct cfs_lstr res;
ENTRY;
+ if (expr->pe_opc != PCC_FIELD_OP_EQ)
+ RETURN(-EINVAL);
+
src.ls_str = str;
src.ls_len = len;
- INIT_LIST_HEAD(fname_list);
+ INIT_LIST_HEAD(&expr->pe_cond);
while (src.ls_str) {
rc = cfs_gettok(&src, ' ', &res);
if (rc == 0) {
rc = -EINVAL;
break;
}
- rc = pcc_fname_list_add(&res, fname_list);
+ rc = pcc_fname_list_add(&res, &expr->pe_cond);
if (rc)
break;
}
if (rc)
- pcc_fname_list_free(fname_list);
+ pcc_fname_list_free(expr);
RETURN(rc);
}
static int
-pcc_id_list_parse(char *str, int len, struct list_head *id_list,
- enum pcc_field type)
+pcc_id_list_parse(char *str, int len, struct pcc_expression *expr)
{
struct cfs_lstr src;
struct cfs_lstr res;
ENTRY;
- if (type != PCC_FIELD_UID && type != PCC_FIELD_GID &&
- type != PCC_FIELD_PROJID)
+ if (expr->pe_field != PCC_FIELD_UID &&
+ expr->pe_field != PCC_FIELD_GID &&
+ expr->pe_field != PCC_FIELD_PROJID)
+ RETURN(-EINVAL);
+
+ if (expr->pe_opc >= PCC_FIELD_OP_MAX)
RETURN(-EINVAL);
src.ls_str = str;
src.ls_len = len;
- INIT_LIST_HEAD(id_list);
+
+ INIT_LIST_HEAD(&expr->pe_cond);
+
while (src.ls_str) {
struct pcc_match_id *id;
__u32 id_val;
GOTO(out, rc = -ENOMEM);
id->pmi_id = id_val;
- list_add_tail(&id->pmi_linkage, id_list);
+ list_add_tail(&id->pmi_linkage, &expr->pe_cond);
}
out:
if (rc)
- pcc_id_list_free(id_list);
+ pcc_id_list_free(expr);
RETURN(rc);
}
+static int
+pcc_expr_id_parse(char *str, int len, struct pcc_expression *expr)
+{
+ int rc = 0;
+
+ if (expr->pe_opc == PCC_FIELD_OP_EQ)
+ rc = pcc_id_list_parse(str, len, expr);
+ else if (!cfs_str2num_check(str, len, &expr->pe_id, 0, (u32)~0U))
+ rc = -EINVAL;
+
+ return rc;
+}
+
+static int
+pcc_size_list_parse(char *str, int len, struct pcc_expression *expr)
+{
+ struct cfs_lstr src;
+ struct cfs_lstr res;
+ int rc = 0;
+
+ ENTRY;
+
+ if (expr->pe_field != PCC_FIELD_SIZE)
+ RETURN(-EINVAL);
+
+ if (expr->pe_opc >= PCC_FIELD_OP_MAX)
+ RETURN(-EINVAL);
+
+ src.ls_str = str;
+ src.ls_len = len;
+
+ INIT_LIST_HEAD(&expr->pe_cond);
+
+ while (src.ls_str) {
+ struct pcc_match_size *sz;
+ __u64 sz_val;
+
+ if (cfs_gettok(&src, ' ', &res) == 0)
+ GOTO(out, rc = -EINVAL);
+
+ rc = sysfs_memparse(res.ls_str, res.ls_len, &sz_val, "MiB");
+ if (rc < 0)
+ GOTO(out, rc = rc);
+
+ OBD_ALLOC_PTR(sz);
+ if (sz == NULL)
+ GOTO(out, rc = -ENOMEM);
+
+ sz->pms_size = sz_val;
+ list_add_tail(&sz->pms_linkage, &expr->pe_cond);
+ }
+out:
+ if (rc)
+ pcc_id_list_free(expr);
+ RETURN(rc);
+}
+
+static int
+pcc_expr_size_parse(char *str, int len, struct pcc_expression *expr)
+{
+ if (expr->pe_opc == PCC_FIELD_OP_EQ)
+ return pcc_size_list_parse(str, len, expr);
+ else
+ return sysfs_memparse(str, len, &expr->pe_size, "MiB");
+}
+
static inline bool
pcc_check_field(struct cfs_lstr *field, char *str)
{
strncmp(field->ls_str, str, len) == 0);
}
+static inline char
+pcc_get_opcode_delim(enum pcc_field_op opc)
+{
+ switch (opc) {
+ case PCC_FIELD_OP_EQ:
+ return PCC_EXPRESSION_DELIM_EQ;
+ case PCC_FIELD_OP_LT:
+ return PCC_EXPRESSION_DELIM_LT;
+ case PCC_FIELD_OP_GT:
+ return PCC_EXPRESSION_DELIM_GT;
+ default:
+ LBUG();
+ }
+}
+
+static enum pcc_field_op
+pcc_get_field_opcode(struct cfs_lstr *src, struct cfs_lstr *field)
+{
+ struct cfs_lstr tmp;
+ int rc;
+ int i;
+
+ ENTRY;
+
+ for (i = PCC_FIELD_OP_EQ; i < PCC_FIELD_OP_MAX; i++) {
+ tmp = *src;
+ rc = cfs_gettok(&tmp, pcc_get_opcode_delim(i), field);
+ if (rc > 0 && tmp.ls_str != NULL) {
+ src->ls_str = tmp.ls_str;
+ src->ls_len = tmp.ls_len;
+ RETURN(i);
+ }
+ }
+
+ RETURN(PCC_FIELD_OP_INV);
+}
+
static int
pcc_expression_parse(struct cfs_lstr *src, struct list_head *cond_list)
{
struct pcc_expression *expr;
+ enum pcc_field_op opc;
struct cfs_lstr field;
- int rc = 0;
+ int rc;
+
+ opc = pcc_get_field_opcode(src, &field);
+ if (opc == PCC_FIELD_OP_INV)
+ return -EINVAL;
+
+ if (src->ls_len <= 2 || src->ls_str[0] != '{' ||
+ src->ls_str[src->ls_len - 1] != '}')
+ return -EINVAL;
OBD_ALLOC_PTR(expr);
if (expr == NULL)
return -ENOMEM;
- rc = cfs_gettok(src, PCC_EXPRESSION_DELIM, &field);
- if (rc == 0 || src->ls_len <= 2 || src->ls_str[0] != '{' ||
- src->ls_str[src->ls_len - 1] != '}')
- GOTO(out, rc = -EINVAL);
-
/* Skip '{' and '}' */
src->ls_str++;
src->ls_len -= 2;
+ expr->pe_opc = opc;
if (pcc_check_field(&field, "uid")) {
- if (pcc_id_list_parse(src->ls_str,
- src->ls_len,
- &expr->pe_cond,
- PCC_FIELD_UID) < 0)
- GOTO(out, rc = -EINVAL);
expr->pe_field = PCC_FIELD_UID;
- } else if (pcc_check_field(&field, "gid")) {
- if (pcc_id_list_parse(src->ls_str,
- src->ls_len,
- &expr->pe_cond,
- PCC_FIELD_GID) < 0)
+ if (pcc_expr_id_parse(src->ls_str, src->ls_len, expr) < 0)
GOTO(out, rc = -EINVAL);
+ } else if (pcc_check_field(&field, "gid")) {
expr->pe_field = PCC_FIELD_GID;
- } else if (pcc_check_field(&field, "projid")) {
- if (pcc_id_list_parse(src->ls_str,
- src->ls_len,
- &expr->pe_cond,
- PCC_FIELD_PROJID) < 0)
+ if (pcc_expr_id_parse(src->ls_str, src->ls_len, expr) < 0)
GOTO(out, rc = -EINVAL);
+ } else if (pcc_check_field(&field, "projid")) {
expr->pe_field = PCC_FIELD_PROJID;
+ if (pcc_expr_id_parse(src->ls_str, src->ls_len, expr) < 0)
+ GOTO(out, rc = -EINVAL);
+ } else if (pcc_check_field(&field, "size")) {
+ expr->pe_field = PCC_FIELD_SIZE;
+ if (pcc_expr_size_parse(src->ls_str, src->ls_len, expr) < 0)
+ GOTO(out, rc = -EINVAL);
} else if (pcc_check_field(&field, "fname")) {
- if (pcc_fname_list_parse(src->ls_str,
- src->ls_len,
- &expr->pe_cond) < 0)
+ if (opc != PCC_FIELD_OP_EQ)
GOTO(out, rc = -EINVAL);
+
expr->pe_field = PCC_FIELD_FNAME;
+ if (pcc_fname_list_parse(src->ls_str, src->ls_len, expr) < 0)
+ GOTO(out, rc = -EINVAL);
} else {
GOTO(out, rc = -EINVAL);
}
}
static int
+pcc_expr_id_match(struct pcc_expression *expr, __u32 id)
+{
+ switch (expr->pe_opc) {
+ case PCC_FIELD_OP_EQ:
+ return pcc_id_list_match(&expr->pe_cond, id);
+ case PCC_FIELD_OP_LT:
+ return id < expr->pe_id;
+ case PCC_FIELD_OP_GT:
+ return id > expr->pe_id;
+ default:
+ return 0;
+ }
+}
+
+static int
+pcc_size_list_match(struct list_head *id_list, __u64 sz_val)
+{
+ struct pcc_match_size *sz;
+
+ list_for_each_entry(sz, id_list, pms_linkage) {
+ if (sz->pms_size == sz_val)
+ return 1;
+ }
+ return 0;
+}
+
+static int
+pcc_expr_size_match(struct pcc_expression *expr, __u64 sz)
+{
+ switch (expr->pe_opc) {
+ case PCC_FIELD_OP_EQ:
+ return pcc_size_list_match(&expr->pe_cond, sz);
+ case PCC_FIELD_OP_LT:
+ return sz < expr->pe_size;
+ case PCC_FIELD_OP_GT:
+ return sz > expr->pe_size;
+ default:
+ return 0;
+ }
+}
+
+static int
pcc_expression_match(struct pcc_expression *expr, struct pcc_matcher *matcher)
{
switch (expr->pe_field) {
case PCC_FIELD_UID:
- return pcc_id_list_match(&expr->pe_cond, matcher->pm_uid);
+ return pcc_expr_id_match(expr, matcher->pm_uid);
case PCC_FIELD_GID:
- return pcc_id_list_match(&expr->pe_cond, matcher->pm_gid);
+ return pcc_expr_id_match(expr, matcher->pm_gid);
case PCC_FIELD_PROJID:
- return pcc_id_list_match(&expr->pe_cond, matcher->pm_projid);
+ return pcc_expr_id_match(expr, matcher->pm_projid);
+ case PCC_FIELD_SIZE:
+ return pcc_expr_size_match(expr, matcher->pm_size);
case PCC_FIELD_FNAME:
return pcc_fname_list_match(&expr->pe_cond,
matcher->pm_name->name);
item.pm_gid = from_kgid(&init_user_ns, current_gid());
item.pm_projid = ll_i2info(inode)->lli_projid;
item.pm_name = &dentry->d_name;
+ item.pm_size = ll_i2info(inode)->lli_lazysize;
dataset = pcc_dataset_match_get(&ll_i2sbi(inode)->ll_pcc_super,
LU_PCC_READONLY, &item);
if (dataset == NULL)
[ -z "$param" ] && param="projid={100}\ rwid=$HSM_ARCHIVE_NUMBER"
stack_trap "cleanup_pcc_mapping $facet" EXIT
- do_facet $facet $LCTL pcc add $MOUNT $hsm_root -p $param
+ do_facet $facet $LCTL pcc add $MOUNT $hsm_root -p "$param" ||
+ error "Setup PCC backend $hsm_root on $MOUNT failed"
}
umount_loopdev() {
}
run_test 32 "Test for RO-PCC when PCC copy is deleted"
+test_33() {
+ local loopfile="$TMP/$tfile"
+ local mntpt="/mnt/pcc.$tdir"
+ local hsm_root="$mntpt/$tdir"
+ local file=$DIR/myfile.doc
+ local file2=$DIR2/myfile.doc
+
+ setup_loopdev $SINGLEAGT $loopfile $mntpt 50
+ copytool setup -m "$MOUNT" -a "$HSM_ARCHIVE_NUMBER"
+
+ setup_pcc_mapping $SINGLEAGT \
+ "fname={*.doc}\&size\<{1M}\ roid=$HSM_ARCHIVE_NUMBER\ ropcc=1"
+ do_facet $SINGLEAGT $LCTL pcc list $MOUNT
+ touch $file || error "touch $file failed"
+ $TRUNCATE $file $((1048576 * 2)) || error "Truncate $file failed"
+ check_lpcc_state $file "none"
+ do_facet $SINGLEAGT $LFS pcc state $file
+ $TRUNCATE $file $((1048576 / 2)) || error "Truncate $file failed"
+ do_facet $SINGLEAGT $LFS pcc state $file
+ check_lpcc_state $file "readonly"
+ cleanup_pcc_mapping
+
+ setup_pcc_mapping $SINGLEAGT \
+ "fname={*.doc}\&size\<{5M}\&size\>{3M}\ roid=5\ ropcc=1"
+ do_facet $SINGLEAGT $LCTL pcc list $MOUNT
+ check_lpcc_state $file "none"
+ $TRUNCATE $file2 $((1048576 * 6)) || error "Truncate $file2 failed"
+ check_lpcc_state $file "none"
+ $TRUNCATE $file2 $((1048576 * 4)) || error "Truncate $file2 failed"
+ check_lpcc_state $file "readonly"
+ cleanup_pcc_mapping
+
+ setup_pcc_mapping $SINGLEAGT \
+ "fname={*.doc}\&size={5M\ 3M}\ roid=5\ ropcc=1"
+ do_facet $SINGLEAGT $LCTL pcc list $MOUNT
+ check_lpcc_state $file "none"
+ $TRUNCATE $file $((1048576 * 5)) || error "Truncate $file failed"
+ check_lpcc_state $file "readonly"
+ do_facet $SINGLEAGT $LFS pcc detach $file ||
+ error "failed to detach $file"
+ $TRUNCATE $file $((1048576 * 4)) || error "Truncate $file failed"
+ check_lpcc_state $file "none"
+ $TRUNCATE $file $((1048576 * 3)) || error "Truncate $file failed"
+ check_lpcc_state $file "readonly"
+ cleanup_pcc_mapping
+}
+run_test 33 "Cache rule with comparator (>, =, <) for file size"
+
+test_34() {
+ local loopfile="$TMP/$tfile"
+ local mntpt="/mnt/pcc.$tdir"
+ local hsm_root="$mntpt/$tdir"
+ local file=$DIR/$tfile
+
+ ! is_project_quota_supported &&
+ skip "project quota is not supported"
+
+ enable_project_quota
+ setup_loopdev $SINGLEAGT $loopfile $mntpt 50
+ copytool setup -m "$MOUNT" -a "$HSM_ARCHIVE_NUMBER"
+
+ setup_pcc_mapping $SINGLEAGT \
+ "projid\>{100}\ roid=5\ ropcc=1"
+ do_facet $SINGLEAGT $LCTL pcc list $MOUNT
+ do_facet $SINGLEAGT "echo -n QQQQQ > $file" ||
+ error "failed to write $file"
+ check_lpcc_state $file "none"
+ $LFS project -p 99 $file || error "failed to set project for $file"
+ $LFS project -d $file
+ check_lpcc_state $file "none"
+ $LFS project -p 101 $file || error "failed to set project for $file"
+ $LFS project -d $file
+ check_lpcc_state $file "readonly"
+ cleanup_pcc_mapping
+
+ setup_pcc_mapping $SINGLEAGT \
+ "projid\<{100}\ roid=5\ ropcc=1"
+ do_facet $SINGLEAGT $LCTL pcc list $MOUNT
+ check_lpcc_state $file "none"
+ $LFS project -p 102 $file || error "failed to set project for $file"
+ $LFS project -d $file
+ check_lpcc_state $file "none"
+ $LFS project -p 99 $file || error "failed to set project for $file"
+ $LFS project -d $file
+ check_lpcc_state $file "readonly"
+ cleanup_pcc_mapping
+
+ setup_pcc_mapping $SINGLEAGT \
+ "projid\<{120}\&projid\>{110}\ roid=5\ ropcc=1"
+ do_facet $SINGLEAGT $LCTL pcc list $MOUNT
+ check_lpcc_state $file "none"
+ $LFS project -p 105 $file || error "failed to set project for $file"
+ $LFS project -d $file
+ check_lpcc_state $file "none"
+ $LFS project -p 121 $file || error "failed to set project for $file"
+ $LFS project -d $file
+ check_lpcc_state $file "none"
+ $LFS project -p 115 $file || error "failed to set project for $file"
+ $LFS project -d $file
+ check_lpcc_state $file "readonly"
+ cleanup_pcc_mapping
+}
+run_test 34 "Cache rule with comparator (>, <) for Project ID range"
+
complete $SECONDS
check_and_cleanup_lustre
exit_status