llapi_pccdev_set.3 \
llapi_pcc_state_get.3 \
llapi_pcc_state_get_fd.3 \
+ llapi_pcc_clear.3 \
+ llapi_pcc_del.3 \
llapi_quotactl.3 \
llapi_rmfid.3 \
llapi_rmfid_at.3 \
.SH SYNOPSIS
.B lctl pcc add \fR<\fImntpath\fR> <\fIpccpath\fR> [\fB--param\fR|\fB-p\fR <\fIparam\fR>]
.br
-.B lctl pcc del <\fImntpath\fR> <\fIpccpath\fR>
+.B lctl pcc del [\fB--keep\fR|\fB-k\fR] <\fImntpath\fR> <\fIpccpath\fR>
.br
-.B lctl pcc clear <\fImntpath\fR>
+.B lctl pcc clear [\fB--keep\fR|\fB-k\fR] <\fImntpath\fR>
.br
.B lctl pcc list <\fImntpath\fR>
.SH DESCRIPTION
attach id (2) which value is same as the archive ID of the copytool agent
running on this PCC node.
.TP
-.B lctl pcc del <\fImntpath\fR> <\fIpccpath\fR>
+.B lctl pcc del [\fB--keep\fR|\fB-k\fR] <\fImntpath\fR> <\fIpccpath\fR>
Delete a PCC backend specified by path
.IR pccpath
on a Lustre client referenced by the mount point of
.IR mntpath .
.TP
-.B lctl pcc clear <\fImntpath\fR>
+.B lctl pcc clear [\fB--keep\fR|\fB-k\fR] <\fImntpath\fR>
Remove all PCC backend on a Lustre client referenced by the mount point of
.IR mntpath .
.TP
.B --param | -p
Specifies the configuration parameters for a PCC backend.
.TP
+.B --keep | -k
+By default, when remove a PCC backend from a client, the action is to scan the
+PCC backend fs, and then uncache (detach and remove) all scanned PCC copies
+from PCC by FIDs. With the option "--keep|-k", the "lctl pcc del|clear" command
+just removes the PCC backend from the Lustre client, and retains the data on the
+cache. In this way, the PCC-RW backend falls back as a tranditional HSM storage
+solution since the copytool is still running at this client.
+.TP
.SH SEE ALSO
.BR lfs (1),
.BR lfs-hsm (1),
--- /dev/null
+.TH llapi_pcc_clear 3 "2019 June 20" "Lustre User API"
+.SH NAME
+llapi_pcc_clear \- Remove all PCC backends from a client
+.SH SYNOPSIS
+.nf
+.B #include <lustre/lustreapi.h>
+.PP
+.BI "int llapi_pcc_clear(const char *" mntpath ,
+.BI " enum lu_pcc_cleanup_flags " flags );
+.fi
+.SH DESCRIPTION
+.PP
+The function
+.BR llapi_pcc_clear()
+deletes all PCC backends on the client with the mount point referenced by
+.IR mntpath .
+Refer
+.BR llapi_pcc_del()
+for the usage of the input parameter
+.IR flags .
+.SH RETURN VALUES
+.PP
+.B llapi_pcc_clear()
+returns 0 on success or a negative errno on failure.
+.SH ERRORS
+.TP 15
+.SM -ENOMEM
+Insufficient memory to complete operation.
+.TP
+.SM -EFAULT
+Memory region is not properly mapped.
+.TP
+.SM -EINVAL
+One or more invalid arguments are given.
+.TP
+.SM -EOPNOTSUPP
+PCC backend operation is not supported.
+.SH "SEE ALSO"
+.BR llapi_pcc_del (3),
+.BR lustreapi (7)
--- /dev/null
+.TH llapi_pcc_del 3 "2019 June 20" "Lustre User API"
+.SH NAME
+llapi_pcc_del \- Delete a PCC backend from a client
+.SH SYNOPSIS
+.nf
+.B #include <lustre/lustreapi.h>
+.PP
+.BI "int llapi_pcc_del(const char *" mntpath ", const char *" pccpath ,
+.BI " enum lu_pcc_cleanup_flags " flags );
+.fi
+.SH DESCRIPTION
+.PP
+The function
+.BR llapi_pcc_del()
+deletes a PCC backend referenced by
+.IR pccpath
+on the client with the mount point of
+.IR mntpath .
+By default, when remove a PCC backend from a client, the action is to scan the
+PCC backend fs, uncache (detach and remove) all scanned PCC copies from PCC by
+FIDs. The input parameter
+.IR flags
+currently only has one value
+.B PCC_CLEANUP_FL_KEEP_DATA
+that means it removes the PCC backend from the Lustre client, but retains
+the data on the cache. In this case, the PCC-RW backend falls back as a
+traditional HSM storage solution and the file data is still accessible as long
+as the copytool is still running at this client.
+.SH RETURN VALUES
+.PP
+.B llapi_pcc_del()
+returns 0 on success or a negative errno value on failure.
+.SH ERRORS
+.TP 15
+.SM -ENOMEM
+Insufficient memory to complete operation.
+.TP
+.SM -EFAULT
+Memory region is not properly mapped.
+.TP
+.SM -EINVAL
+One or more invalid arguments are given.
+.TP
+.SM -EOPNOTSUPP
+PCC backend operation is not supported.
+.SH "SEE ALSO"
+.BR lustreapi (7)
.nf
.B #include <lustre/lustreapi.h>
.PP
-.BI "int llapi_pcc_detach_fid_fd(int " dirfd ", const struct lu_fid *" fid ");"
+.BI "int llapi_pcc_detach_fid_fd(int " dirfd ", const struct lu_fid *" fid ",
+.BI " __u32 " flags );
.PP
-.BI "int llapi_pcc_detach_fid(const char *" mntpath ", const struct lu_fid *" fid ");"
+.BI "int llapi_pcc_detach_fid(const char *" mntpath ",
+.BI " const struct lu_fid *" fid ", __u32 " flags );
.PP
-.BI "int llapi_pcc_detach_fid_str(const char *" mntpath ", const char *" fidstr ");"
+.BI "int llapi_pcc_detach_fid_str(const char *" mntpath ",
+.BI " const char *" fidstr ", __u32 " flags );
.PP
-.BI "int llapi_pcc_detach_file(const char *" path ");"
+.BI "int llapi_pcc_detach_file(const char *" path ", __u32 " flags );
.fi
.SH DESCRIPTION
.PP
.BR llapi_pcc_detach_fid_str() ,
and
.BR llapi_pcc_detach_file()
-detaches a cached file from PCC by an ioctl on the dir. The file is referenced
+detach a cached file from PCC by an ioctl on the dir or the file itself. The
+file is referenced
by
.IR fid ,
.IR fidstr ,
.IR dirfd ,
.IR mntpath ,
.IR path .
+The detach flags is specified by
+.IR flags
+argument, which is a
+.B enum lu_pcc_detach_flags
+data structure, which contains the following values:
+.nf
+.LP
+ PCC_DETACH_FL_NONE = 0x0,
+ PCC_DETACH_FL_UNCACHE = 0x01,
+ PCC_DETACH_FL_KNOWN_READWRITE = 0x02,
+ PCC_DETACH_FL_KNOWN_READONLY = 0x04,
+ PCC_DETACH_FL_CACHE_REMOVED = 0x08,
+.fi
+.TP
+PCC_DETACH_FL_NONE
+means that detach the file from PCC yet retain the data copy on PCC backend.
+.TP
+PCC_DETACH_FL_UNCACHE
+means that remove the PCC copy after detach.
+.TP
+PCC_DETACH_KNOWN_READWRITE
+means that it is known that the file was once cached as PCC-RW.
+.TP
+PCC_DETACH_KNOWN_READONLY
+means that it is known that the file was once cached as PCC-RO.
+.TP
+PCC_DETACH_FL_CACHE_REMOVED
+indicates that PCC cached copy is removed. It is used to tell the user space
+caller that the file is detached and the corresponding PCC copy is removed.
.SH RETURN VALUES
.LP
.BR llapi_pcc_detach_fid_fd() ,
__u32 id, enum lu_pcc_type type);
int llapi_pcc_attach_fid_str(const char *mntpath, const char *fidstr,
__u32 id, enum lu_pcc_type type);
-int llapi_pcc_detach_fd(int fd, __u32 option);
+int llapi_pcc_detach_fd(int fd, __u32 flags);
int llapi_pcc_detach_fid(const char *mntpath, const struct lu_fid *fid,
- __u32 option);
+ __u32 flags);
int llapi_pcc_detach_fid_str(const char *mntpath, const char *fidstr,
- __u32 option);
-int llapi_pcc_detach_file(const char *path, __u32 option);
+ __u32 flags);
+int llapi_pcc_detach_file(const char *path, __u32 flags);
int llapi_pcc_state_get_fd(int fd, struct lu_pcc_state *state);
int llapi_pcc_state_get(const char *path, struct lu_pcc_state *state);
int llapi_pccdev_set(const char *mntpath, const char *cmd);
int llapi_pccdev_get(const char *mntpath);
+int llapi_pcc_del(const char *mntpath, const char *pccpath, __u32 flags);
+int llapi_pcc_clear(const char *mntpath, __u32 flags);
/** @} llapi */
/* llapi_layout user interface */
#define LL_IOC_HEAT_GET _IOWR('f', 251, struct lu_heat)
#define LL_IOC_HEAT_SET _IOW('f', 251, __u64)
#define LL_IOC_PCC_ATTACH _IOW('f', 252, struct lu_pcc_attach)
-#define LL_IOC_PCC_DETACH _IOW('f', 252, struct lu_pcc_detach)
-#define LL_IOC_PCC_DETACH_BY_FID _IOW('f', 252, struct lu_pcc_detach_fid)
+#define LL_IOC_PCC_DETACH _IOWR('f', 252, struct lu_pcc_detach)
+#define LL_IOC_PCC_DETACH_BY_FID _IOWR('f', 252, \
+ struct lu_pcc_detach_fid)
#define LL_IOC_PCC_STATE _IOR('f', 252, struct lu_pcc_state)
#define LL_IOC_PROJECT _IOW('f', 253, struct lu_project)
}
}
+#define PCC_YAML_PCCPATH "pccpath"
+#define PCC_YAML_HSMTOOL "hsmtool"
+#define PCC_YAML_RWID "rwid"
+#define PCC_YAML_ROID "roid"
+#define PCC_YAML_FLAGS "flags"
+#define PCC_YAML_AUTOCACHE "autocache"
+
+enum hsmtool_type {
+ HSMTOOL_UNKNOWN = 0,
+ /*
+ * v1 (original) using 6 directories (oid & 0xffff)/-/-/-/-/-/FID.
+ * Places only one FID per directory. See ct_path_archive() below.
+ */
+ HSMTOOL_POSIX_V1 = 1,
+ /* v2 using (OID & 0xffff)^(SEQ & 0xffff)/FID. */
+ HSMTOOL_POSIX_V2 = 2,
+ HSMTOOL_DEFAULT = HSMTOOL_POSIX_V2,
+};
+
+static inline const char *hsmtool_type2string(enum hsmtool_type type)
+{
+ switch (type) {
+ case HSMTOOL_POSIX_V1:
+ return "posix_v1";
+ case HSMTOOL_POSIX_V2:
+ return "posix_v2";
+ default:
+ return "unknown";
+ }
+}
+
+static inline enum hsmtool_type hsmtool_string2type(const char *str)
+{
+ if (strcmp(str, "posix") == 0)
+ return HSMTOOL_DEFAULT;
+ if (strcmp(str, "posix_v1") == 0)
+ return HSMTOOL_POSIX_V1;
+ if (strcmp(str, "posix_v2") == 0)
+ return HSMTOOL_POSIX_V2;
+
+ return HSMTOOL_UNKNOWN;
+}
+
struct lu_pcc_attach {
__u32 pcca_type; /* PCC type */
__u32 pcca_id; /* Attach ID */
};
-enum lu_pcc_detach_opts {
- PCC_DETACH_OPT_NONE = 0, /* Detach only, keep the PCC copy */
- PCC_DETACH_OPT_UNCACHE, /* Remove the cached file after detach */
+enum lu_pcc_detach_flags {
+ /* Detach only, keep the PCC copy */
+ PCC_DETACH_FL_NONE = 0x0,
+ /* Remove the cached file after detach */
+ PCC_DETACH_FL_UNCACHE = 0x01,
+ /* Known the file was once used as PCC-RW */
+ PCC_DETACH_FL_KNOWN_READWRITE = 0x02,
+ /* Known the file was once used as PCC-RO */
+ PCC_DETACH_FL_KNOWN_READONLY = 0x04,
+ /* Indicate PCC cached copy is removed */
+ PCC_DETACH_FL_CACHE_REMOVED = 0x08,
};
struct lu_pcc_detach_fid {
/* fid of the file to detach */
struct lu_fid pccd_fid;
- __u32 pccd_opt;
+ __u32 pccd_flags;
};
struct lu_pcc_detach {
- __u32 pccd_opt;
+ __u32 pccd_flags;
};
enum lu_pcc_state_flags {
char pccs_path[PATH_MAX];
};
+enum lu_pcc_cleanup_flags {
+ PCC_CLEANUP_FL_NONE = 0x0,
+ /* Remove the PCC backend but retain the data on the cache */
+ PCC_CLEANUP_FL_KEEP_DATA = 0x1,
+};
+
enum lu_project_type {
LU_PROJECT_NONE = 0,
LU_PROJECT_SET,
if (!inode_owner_or_capable(&nop_mnt_idmap, inode2))
GOTO(out_iput, rc = -EPERM);
- rc = pcc_ioctl_detach(inode2, detach->pccd_opt);
+ rc = pcc_ioctl_detach(inode2, &detach->pccd_flags);
+ if (rc)
+ GOTO(out_iput, rc);
+
+ if (copy_to_user((char __user *)arg, detach, sizeof(*detach)))
+ GOTO(out_iput, rc = -EFAULT);
out_iput:
iput(inode2);
out_detach:
if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
GOTO(out_detach_free, rc = -EPERM);
- rc = pcc_ioctl_detach(inode, detach->pccd_opt);
+ rc = pcc_ioctl_detach(inode, &detach->pccd_flags);
+ if (rc)
+ GOTO(out_detach_free, rc);
+
+ if (copy_to_user((char __user *)arg, detach, sizeof(*detach)))
+ GOTO(out_detach_free, rc = -EFAULT);
out_detach_free:
OBD_FREE_PTR(detach);
RETURN(rc);
return rc;
if (id > 0)
cmd->u.pccc_add.pccc_flags |= PCC_DATASET_PCCRO;
+ } else if (strcmp(key, "hsmtool") == 0) {
+ cmd->u.pccc_add.pccc_hsmtool_type = hsmtool_string2type(val);
+ if (cmd->u.pccc_add.pccc_hsmtool_type != HSMTOOL_POSIX_V1 &&
+ cmd->u.pccc_add.pccc_hsmtool_type != HSMTOOL_POSIX_V2)
+ return -EINVAL;
} else {
return -EINVAL;
}
switch (cmd->pccc_cmd) {
case PCC_ADD_DATASET:
+ cmd->u.pccc_add.pccc_hsmtool_type = HSMTOOL_UNKNOWN;
/* Enable auto attach by default */
cmd->u.pccc_add.pccc_flags |= PCC_DATASET_AUTO_ATTACH;
break;
cmd->u.pccc_add.pccc_flags & PCC_DATASET_PCCRO)
cmd->u.pccc_add.pccc_roid = cmd->u.pccc_add.pccc_rwid;
+ if (cmd->u.pccc_add.pccc_hsmtool_type == HSMTOOL_UNKNOWN)
+ cmd->u.pccc_add.pccc_hsmtool_type = HSMTOOL_DEFAULT;
+
return 0;
}
dataset->pccd_rwid = cmd->u.pccc_add.pccc_rwid;
dataset->pccd_roid = cmd->u.pccc_add.pccc_roid;
dataset->pccd_flags = cmd->u.pccc_add.pccc_flags;
+ dataset->pccd_hsmtool_type = cmd->u.pccc_add.pccc_hsmtool_type;
atomic_set(&dataset->pccd_refcount, 1);
rc = pcc_dataset_rule_init(&dataset->pccd_rule, cmd);
static void
pcc_dataset_dump(struct pcc_dataset *dataset, struct seq_file *m)
{
- seq_printf(m, "%s:\n", dataset->pccd_pathname);
- seq_printf(m, " rwid: %u\n", dataset->pccd_rwid);
- seq_printf(m, " flags: %x\n", dataset->pccd_flags);
- seq_printf(m, " autocache: %s\n", dataset->pccd_rule.pmr_conds_str);
+ seq_puts(m, " -\n");
+ seq_printf(m, " " PCC_YAML_PCCPATH ": %s\n",
+ dataset->pccd_pathname);
+ seq_printf(m, " " PCC_YAML_HSMTOOL ": %s\n",
+ hsmtool_type2string(dataset->pccd_hsmtool_type));
+ seq_printf(m, " " PCC_YAML_RWID ": %u\n", dataset->pccd_rwid);
+ seq_printf(m, " " PCC_YAML_ROID ": %u\n", dataset->pccd_roid);
+ seq_printf(m, " " PCC_YAML_FLAGS ": %x\n", dataset->pccd_flags);
+ seq_printf(m, " " PCC_YAML_AUTOCACHE ": %s\n",
+ dataset->pccd_rule.pmr_conds_str);
}
int
struct pcc_dataset *dataset;
down_read(&super->pccs_rw_sem);
+ if (!list_empty(&super->pccs_datasets))
+ seq_puts(m, "pcc:\n");
list_for_each_entry(dataset, &super->pccs_datasets, pccd_linkage) {
pcc_dataset_dump(dataset, m);
}
}
/*
- * TODO:
+ * Add HSMTOOL_POSIX_V2 support.
* As Andreas suggested, we'd better use new layout to
* reduce overhead:
* (fid->f_oid >> 16 & oxFFFF)/FID
*/
#define PCC_DATASET_MAX_PATH (6 * 5 + FID_NOBRACE_LEN + 1)
-static int pcc_fid2dataset_path(char *buf, int sz, struct lu_fid *fid)
-{
- return scnprintf(buf, sz, "%04x/%04x/%04x/%04x/%04x/%04x/"
- DFID_NOBRACE,
- (fid)->f_oid & 0xFFFF,
- (fid)->f_oid >> 16 & 0xFFFF,
- (unsigned int)((fid)->f_seq & 0xFFFF),
- (unsigned int)((fid)->f_seq >> 16 & 0xFFFF),
- (unsigned int)((fid)->f_seq >> 32 & 0xFFFF),
- (unsigned int)((fid)->f_seq >> 48 & 0xFFFF),
- PFID(fid));
+static int pcc_fid2dataset_path(struct pcc_dataset *dataset, char *buf,
+ int sz, struct lu_fid *fid)
+{
+ switch (dataset->pccd_hsmtool_type) {
+ case HSMTOOL_POSIX_V1:
+ return snprintf(buf, sz, "%04x/%04x/%04x/%04x/%04x/%04x/"
+ DFID_NOBRACE,
+ (fid)->f_oid & 0xFFFF,
+ (fid)->f_oid >> 16 & 0xFFFF,
+ (unsigned int)((fid)->f_seq & 0xFFFF),
+ (unsigned int)((fid)->f_seq >> 16 & 0xFFFF),
+ (unsigned int)((fid)->f_seq >> 32 & 0xFFFF),
+ (unsigned int)((fid)->f_seq >> 48 & 0xFFFF),
+ PFID(fid));
+ case HSMTOOL_POSIX_V2:
+ return snprintf(buf, sz, "%04x/"DFID_NOBRACE,
+ (__u32)((fid)->f_oid ^ (fid)->f_seq) & 0XFFFF,
+ PFID(fid));
+ default:
+ return -EINVAL;
+ }
}
static inline const struct cred *pcc_super_cred(struct super_block *sb)
!(dataset->pccd_flags & PCC_DATASET_PCCRO))
RETURN(0);
- rc = pcc_fid2dataset_path(pathname, PCC_DATASET_MAX_PATH,
+ rc = pcc_fid2dataset_path(dataset, pathname, PCC_DATASET_MAX_PATH,
&lli->lli_fid);
old_cred = override_creds(pcc_super_cred(inode->i_sb));
*cached = pcc_io_tolerate(pcci, iot, rc);
if (atomic_dec_and_test(&pcci->pcci_active_ios))
- wake_up(&pcci->pcci_waitq);
+ wake_up_all(&pcci->pcci_waitq);
}
-
static ssize_t
__pcc_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
if (!pcc_vm_ops->page_mkwrite &&
page->mapping == pcc_file->f_mapping) {
+ __u32 flags = PCC_DETACH_FL_UNCACHE;
+
CDEBUG(D_MMAP,
"%s: PCC backend fs not support ->page_mkwrite()\n",
ll_i2sbi(inode)->ll_fsname);
- pcc_ioctl_detach(inode, PCC_DETACH_OPT_UNCACHE);
+ (void) pcc_ioctl_detach(inode, &flags);
mmap_read_unlock(mm);
*cached = true;
RETURN(VM_FAULT_RETRY | VM_FAULT_NOPAGE);
* __do_page_fault and retry the memory fault handling.
*/
if (page->mapping == pcc_file->f_mapping) {
- pcc_ioctl_detach(inode, PCC_DETACH_OPT_UNCACHE);
+ __u32 flags = PCC_DETACH_FL_UNCACHE;
+
+ pcc_ioctl_detach(inode, &flags);
*cached = true;
mmap_read_unlock(mm);
RETURN(VM_FAULT_RETRY | VM_FAULT_NOPAGE);
* Lustre I/O path.
*/
if (rc & VM_FAULT_SIGBUS) {
- pcc_ioctl_detach(inode, PCC_DETACH_OPT_UNCACHE);
+ __u32 flags = PCC_DETACH_FL_UNCACHE;
+
+ (void) pcc_ioctl_detach(inode, &flags);
mmap_read_unlock(mm);
RETURN(VM_FAULT_RETRY | VM_FAULT_NOPAGE);
}
if (path == NULL)
return -ENOMEM;
- pcc_fid2dataset_path(path, PCC_DATASET_MAX_PATH, fid);
+ pcc_fid2dataset_path(dataset, path, PCC_DATASET_MAX_PATH, fid);
base = pcc_mkdir_p(dataset->pccd_path.dentry, path, 0);
if (IS_ERR(base)) {
if (rc) {
CDEBUG(D_CACHE, DFID" RESTORE failure: %d\n",
PFID(&ll_i2info(inode)->lli_fid), rc);
- RETURN(rc);
+ /* ignore the RESTORE failure.
+ * i.e. the file is in exists dirty archived state.
+ */
+ } else {
+ ll_layout_refresh(inode, &gen);
}
- ll_layout_refresh(inode, &gen);
-
len = sizeof(struct hsm_user_request) +
sizeof(struct hsm_user_item);
OBD_ALLOC(hur, len);
RETURN(rc);
}
-int pcc_ioctl_detach(struct inode *inode, __u32 opt)
+int pcc_ioctl_detach(struct inode *inode, __u32 *flags)
{
struct ll_inode_info *lli = ll_i2info(inode);
struct pcc_inode *pcci;
LASSERT(atomic_read(&pcci->pcci_refcount) > 0);
if (pcci->pcci_type == LU_PCC_READWRITE) {
- if (opt == PCC_DETACH_OPT_UNCACHE) {
+ if (*flags & PCC_DETACH_FL_UNCACHE) {
hsm_remove = true;
/*
* The file will be removed from PCC, set the flags
} else if (pcci->pcci_type == LU_PCC_READONLY) {
__pcc_layout_invalidate(pcci);
- if (opt == PCC_DETACH_OPT_UNCACHE && !pcci->pcci_unlinked) {
+ if (*flags & PCC_DETACH_FL_UNCACHE && !pcci->pcci_unlinked) {
old_cred = override_creds(pcc_super_cred(inode->i_sb));
rc = pcc_inode_remove(inode, pcci->pcci_path.dentry);
revert_creds(old_cred);
- if (!rc)
+ if (!rc) {
pcci->pcci_unlinked = true;
+ *flags |= PCC_DETACH_FL_CACHE_REMOVED;
+ }
}
pcc_inode_put(pcci);
out_unlock:
pcc_inode_unlock(inode);
- if (hsm_remove) {
+
+ if (hsm_remove || (*flags & PCC_DETACH_FL_UNCACHE &&
+ *flags & PCC_DETACH_FL_KNOWN_READWRITE)) {
old_cred = override_creds(pcc_super_cred(inode->i_sb));
rc = pcc_hsm_remove(inode);
revert_creds(old_cred);
+ if (!rc)
+ *flags |= PCC_DETACH_FL_CACHE_REMOVED;
}
RETURN(rc);
struct path pccd_path; /* Root path */
struct list_head pccd_linkage; /* Linked to pccs_datasets */
atomic_t pccd_refcount; /* Reference count */
+ enum hsmtool_type pccd_hsmtool_type; /*HSM copytool type */
};
struct pcc_super {
struct list_head pccc_conds;
char *pccc_conds_str;
enum pcc_dataset_flags pccc_flags;
+ enum hsmtool_type pccc_hsmtool_type;
} pccc_add;
struct pcc_cmd_del {
__u32 pccc_pad;
bool attached);
int pcc_ioctl_attach(struct file *file, struct inode *inode,
struct lu_pcc_attach *attach);
-int pcc_ioctl_detach(struct inode *inode, __u32 opt);
+int pcc_ioctl_detach(struct inode *inode, __u32 *flags);
int pcc_ioctl_state(struct file *file, struct inode *inode,
struct lu_pcc_state *state);
void pcc_file_init(struct pcc_file *pccf);
/* Checks for struct lu_pcc_detach */
LASSERTF((int)sizeof(struct lu_pcc_detach) == 4, "found %lld\n",
(long long)(int)sizeof(struct lu_pcc_detach));
- LASSERTF((int)offsetof(struct lu_pcc_detach, pccd_opt) == 0, "found %lld\n",
- (long long)(int)offsetof(struct lu_pcc_detach, pccd_opt));
- LASSERTF((int)sizeof(((struct lu_pcc_detach *)0)->pccd_opt) == 4, "found %lld\n",
- (long long)(int)sizeof(((struct lu_pcc_detach *)0)->pccd_opt));
+ LASSERTF((int)offsetof(struct lu_pcc_detach, pccd_flags) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lu_pcc_detach, pccd_flags));
+ LASSERTF((int)sizeof(((struct lu_pcc_detach *)0)->pccd_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_pcc_detach *)0)->pccd_flags));
/* Checks for struct lu_pcc_detach_fid */
LASSERTF((int)sizeof(struct lu_pcc_detach_fid) == 20, "found %lld\n",
(long long)(int)offsetof(struct lu_pcc_detach_fid, pccd_fid));
LASSERTF((int)sizeof(((struct lu_pcc_detach_fid *)0)->pccd_fid) == 16, "found %lld\n",
(long long)(int)sizeof(((struct lu_pcc_detach_fid *)0)->pccd_fid));
- LASSERTF((int)offsetof(struct lu_pcc_detach_fid, pccd_opt) == 16, "found %lld\n",
- (long long)(int)offsetof(struct lu_pcc_detach_fid, pccd_opt));
- LASSERTF((int)sizeof(((struct lu_pcc_detach_fid *)0)->pccd_opt) == 4, "found %lld\n",
- (long long)(int)sizeof(((struct lu_pcc_detach_fid *)0)->pccd_opt));
+ LASSERTF((int)offsetof(struct lu_pcc_detach_fid, pccd_flags) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct lu_pcc_detach_fid, pccd_flags));
+ LASSERTF((int)sizeof(((struct lu_pcc_detach_fid *)0)->pccd_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_pcc_detach_fid *)0)->pccd_flags));
/* Checks for struct lu_pcc_state */
LASSERTF((int)sizeof(struct lu_pcc_state) == 4112, "found %lld\n",
# UPDATE THE COMMENT ABOVE WITH BUG NUMBERS WHEN CHANGING ALWAYS_EXCEPT!
ENABLE_PROJECT_QUOTAS=${ENABLE_PROJECT_QUOTAS:-true}
-HSMTOOL_ARCHIVE_FORMAT=v1
+HSMTOOL_ARCHIVE_FORMAT=v2
LUSTRE=${LUSTRE:-$(cd $(dirname $0)/..; echo $PWD)}
cleanup_pcc_mapping() {
local facet=${1:-$SINGLEAGT}
+ echo "Cleanup PCC backend on $MOUNT"
do_facet $facet $LCTL pcc clear $MOUNT
}
local mntpt="/mnt/pcc.$tdir"
local hsm_root="$mntpt/$tdir"
local file=$DIR/$tfile
- local file2=$DIR2/$tfile
local fid
$LCTL get_param -n mdc.*.connect_flags | grep -q pcc_ro ||
do_facet $SINGLEAGT $LFS pcc detach -k $file ||
error "failed to detach $file"
check_lpcc_state $file "none"
+
unlink $file || error "unlink $file failed"
}
run_test 21c "Verify HSM release works storing PCC-RO as HSM mirror component"
}
run_test 29b "Auto PCC-RO attach in atomic_open"
+test_30() {
+ local loopfile="$TMP/$tfile"
+ local mntpt="/mnt/pcc.$tdir"
+ local hsm_root="$mntpt/$tdir"
+ local file
+
+ $LCTL get_param -n mdc.*.connect_flags | grep -q pcc_ro ||
+ skip "Server does not support PCC-RO"
+
+ setup_loopdev $SINGLEAGT $loopfile $mntpt 50
+ copytool setup -m "$MOUNT" -a "$HSM_ARCHIVE_NUMBER"
+ setup_pcc_mapping $SINGLEAGT "projid={100}\ rwid=$HSM_ARCHIVE_NUMBER\ pccrw=1\ pccro=1"
+
+ mkdir $DIR/$tdir || error "mkdir $DIR/$tdir failed"
+
+ file=$DIR/$tdir/rwattach
+ echo -n backend_del_attach > $file
+ do_facet $SINGLEAGT $LFS pcc attach -w -i $HSM_ARCHIVE_NUMBER $file ||
+ error "RW-PCC attach $file failed"
+
+ file=$DIR/$tdir/rwattachrm
+ echo -n backend_del_attach_rm > $file
+ do_facet $SINGLEAGT $LFS pcc attach -w -i $HSM_ARCHIVE_NUMBER $file ||
+ error "RW-PCC attach $file failed"
+ rm $file || error "rm $file failed"
+
+ file=$DIR/$tdir/roattach
+ echo -n backend_del_roattach_rm > $file
+ do_facet $SINGLEAGT $LFS pcc attach -r -i $HSM_ARCHIVE_NUMBER $file ||
+ error "RO-PCC attach $file failed"
+
+ do_facet $SINGLEAGT $LCTL pcc list $MOUNT
+ do_facet $SINGLEAGT $LCTL pcc del -v -v -v -v $MOUNT $hsm_root ||
+ error "lctl pcc del $MOUNT $hsm_root failed"
+}
+run_test 30 "Test lctl pcc del command"
+
+test_31() {
+ local loopfile="$TMP/$tfile"
+ local mntpt="/mnt/pcc.$tdir"
+ local hsm_root="$mntpt/$tdir"
+ local -a lpcc_path1
+ local -a lpcc_path2
+ local -a lpcc_path3
+ local file
+
+ $LCTL get_param -n mdc.*.connect_flags | grep -q pcc_ro ||
+ skip "Server does not support PCC-RO"
+
+ setup_loopdev $SINGLEAGT $loopfile $mntpt 50
+ copytool setup -m "$MOUNT" -a "$HSM_ARCHIVE_NUMBER"
+ setup_pcc_mapping $SINGLEAGT \
+ "projid={100}\ rwid=$HSM_ARCHIVE_NUMBER\ auto_attach=0\ pccrw=1\ pccro=1"
+
+ mkdir $DIR/$tdir || error "mkdir $DIR/$tdir failed"
+
+ file=$DIR/$tdir/rwattach
+ echo -n backend_del_attach > $file
+ lpcc_path1=$(lpcc_fid2path $hsm_root $file)
+ do_facet $SINGLEAGT $LFS pcc attach -w -i $HSM_ARCHIVE_NUMBER $file ||
+ error "RW-PCC attach $file failed"
+ check_lpcc_state $file "readwrite"
+ do_facet $SINGLEAGT $LFS pcc detach -k $file ||
+ error "RW-PCC detach $file failed"
+ check_lpcc_state $file "none"
+
+ file=$DIR/$tdir/rwattachrm
+ echo -n backend_del_attach_rm > $file
+ lpcc_path2=$(lpcc_fid2path $hsm_root $file)
+ do_facet $SINGLEAGT $LFS pcc attach -w -i $HSM_ARCHIVE_NUMBER $file ||
+ error "RW-PCC attach $file failed"
+ check_lpcc_state $file "readwrite"
+ do_facet $SINGLEAGT $LFS pcc detach -k $file ||
+ error "RW-PCC detach $file failed"
+ check_lpcc_state $file "none"
+ rm $file || error "rm $file failed"
+
+ file=$DIR/$tdir/roattach
+ echo -n backend_del_roattach_rm > $file
+ lpcc_path3=$(lpcc_fid2path $hsm_root $file "readonly")
+ do_facet $SINGLEAGT $LFS pcc attach -r -i $HSM_ARCHIVE_NUMBER $file ||
+ error "RO-PCC attach $file failed"
+ check_lpcc_state $file "readonly"
+ do_facet $SINGLEAGT $LFS pcc detach -k $file ||
+ error "RO-PCC detach $file failed"
+ check_lpcc_state $file "none"
+
+ do_facet $SINGLEAGT $LCTL pcc list $MOUNT
+ do_facet $SINGLEAGT $LCTL pcc del -v -v -v -v -k $MOUNT $hsm_root ||
+ error "lctl pcc del -k $MOUNT $hsm_root failed"
+
+ do_facet $SINGLEAGT "[ -f $lpcc_path1 ]" ||
+ error "PCC copy $lpcc_path1 should retain"
+ do_facet $SINGLEAGT "[ -f $lpcc_path2 ]" ||
+ error "PCC copy $lpcc_path1 should retain"
+ do_facet $SINGLEAGT "[ -f $lpcc_path3 ]" ||
+ error "PCC copy $lpcc_path1 should retain"
+}
+run_test 31 "Test lctl pcc del command with --keep option"
+
+test_32() {
+ local agt_host=$(facet_active_host $SINGLEAGT)
+ local loopfile="$TMP/$tfile"
+ local mntpt="/mnt/pcc.$tdir"
+ local hsm_root="$mntpt/$tdir"
+ local file=$DIR/$tfile
+ local -a lpcc_path
+
+ $LCTL get_param -n mdc.*.connect_flags | grep -q pcc_ro ||
+ skip "Server does not support PCC-RO"
+
+ setup_loopdev $SINGLEAGT $loopfile $mntpt 50
+ copytool setup -m "$MOUNT" -a "$HSM_ARCHIVE_NUMBER"
+ setup_pcc_mapping $SINGLEAGT \
+ "projid={100}\ rwid=$HSM_ARCHIVE_NUMBER\ auto_attach=0"
+
+ do_facet $SINGLEAGT echo -n roattach_removed > $file
+ lpcc_path=$(lpcc_fid2path $hsm_root $file "readonly")
+ do_facet $SINGLEAGT $LFS pcc attach -r -i $HSM_ARCHIVE_NUMBER $file ||
+ error "RO-PCC attach $file failed"
+ rmultiop_start $agt_host $file o_rc || error "multiop $file failed"
+ sleep 3
+ do_facet $SINGLEAGT rm $lpcc_path || error "rm $lpcc_path failed"
+ rmultiop_stop $agt_host || error "multiop $file read failed"
+ check_lpcc_state $file "readonly"
+
+ local content=$(do_facet $SINGLEAGT cat $file)
+ [[ $content == "roattach_removed" ]] || error "data mismatch: $content"
+ check_lpcc_state $file "readonly"
+ do_facet $SINGLEAGT $LFS pcc detach -k $file ||
+ error "RO-PCC detach $file failed"
+ check_lpcc_state $file "none"
+
+ do_facet $SINGLEAGT $LFS pcc attach -r -i $HSM_ARCHIVE_NUMBER $file ||
+ error "RO-PCC attach $file failed"
+ do_facet $SINGLEAGT rm $lpcc_path || error "rm $lpcc_path failed"
+ check_lpcc_state $file "readonly"
+ content=$(do_facet $SINGLEAGT cat $file)
+ [[ $content == "roattach_removed" ]] || error "data mismatch: $content"
+ check_lpcc_state $file "readonly"
+ do_facet $SINGLEAGT $LFS pcc detach -k $file ||
+ error "RO-PCC detach $file failed"
+ check_lpcc_state $file "none"
+}
+run_test 32 "Test for RO-PCC when PCC copy is deleted"
+
+test_36_base() {
+ local loopfile="$TMP/$tfile"
+ local mntpt="/mnt/pcc.$tdir"
+ local hsm_root="$mntpt/$tdir"
+ local file=$DIR/$tfile
+ local -a lpcc_path
+ local state="readonly"
+ local rw="$1"
+
+ [[ -z $rw ]] || state="readwrite"
+ setup_loopdev $SINGLEAGT $loopfile $mntpt 50
+ copytool setup -m "$MOUNT" -a "$HSM_ARCHIVE_NUMBER"
+ setup_pcc_mapping $SINGLEAGT "projid={100}\ rwid=$HSM_ARCHIVE_NUMBER\ pccrw=1\ pccro=1"
+
+ echo -n backend_clear_verify > $file
+ lpcc_path=$(lpcc_fid2path $hsm_root $file)
+ do_facet $SINGLEAGT $LFS pcc attach $rw -i $HSM_ARCHIVE_NUMBER $file ||
+ error "PCC attach $ro $file failed"
+ check_lpcc_state $file "$state"
+ do_facet $SINGLEAGT $LFS pcc detach -k $file ||
+ error "PCC detach -k $file failed"
+ do_facet $SINGLEAGT "[ -f $lpcc_path1 ]" ||
+ error "PCC copy $lpcc_path should retain"
+ do_facet $SINGLEAGT $LCTL pcc clear -v $MOUNT ||
+ error "lctl pcc clear -v $MOUNT failed"
+ do_facet $SINGLEAGT "[ -f $lpcc_path ]" &&
+ error "PCC copy $lpcc_path should be removed"
+ rm $file || error "rm $file failed"
+}
+
+test_36a() {
+ test_36_base "-w"
+}
+run_test 36a "Stale RW-PCC copy should be deleted after remove the PCC backend"
+
+test_36b() {
+ $LCTL get_param -n mdc.*.connect_flags | grep -q pcc_ro ||
+ skip "Server does not support PCC-RO"
+
+ test_36_base
+}
+run_test 36b "Stale RO-PCC copy should be deleted after remove the PCC backend"
+
#test 101: containers and PCC
#LU-15170: Test mount namespaces with PCC
#This tests the cases where the PCC mount is not present in the container by
liblustreapi_ladvise.c liblustreapi_chlg.c \
liblustreapi_heat.c liblustreapi_pcc.c \
liblustreapi_ioctl.c liblustreapi_root.c \
- liblustreapi_lseek.c liblustreapi_swap.c
+ liblustreapi_lseek.c liblustreapi_swap.c \
+ libhsm_scanner.h libhsm_scanner.c
liblustreapi_la_CFLAGS = -fPIC -D_GNU_SOURCE $(LIBNL3_CFLAGS) \
-I $(top_builddir)/lnet/utils \
-D_LARGEFILE64_SOURCE=1 -D_FILE_OFFSET_BITS=64 \
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'k', .name = "keep", .has_arg = no_argument },
{ .name = NULL } };
+ char short_opts[] = "hk";
int c;
int rc = 0;
const char *path;
char fullpath[PATH_MAX];
- __u32 detach_opt = PCC_DETACH_OPT_UNCACHE;
+ __u32 detach_flags = PCC_DETACH_FL_UNCACHE;
optind = 0;
- while ((c = getopt_long(argc, argv, "hk",
+ while ((c = getopt_long(argc, argv, short_opts,
long_opts, NULL)) != -1) {
switch (c) {
case 'k':
- detach_opt = PCC_DETACH_OPT_NONE;
+ detach_flags = PCC_DETACH_FL_NONE;
break;
default:
fprintf(stderr, "%s: unrecognized option '%s'\n",
continue;
}
- rc2 = llapi_pcc_detach_file(fullpath, detach_opt);
+ rc2 = llapi_pcc_detach_file(fullpath, detach_flags);
if (rc2 < 0) {
rc2 = -errno;
fprintf(stderr,
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'k', .name = "keep", .has_arg = no_argument },
{ .name = NULL } };
+ char short_opts[] = "hk";
int c;
int rc = 0;
const char *fid;
const char *mntpath;
- __u32 detach_opt = PCC_DETACH_OPT_UNCACHE;
+ __u32 detach_flags = PCC_DETACH_FL_UNCACHE;
optind = 0;
- while ((c = getopt_long(argc, argv, "hk",
+ while ((c = getopt_long(argc, argv, short_opts,
long_opts, NULL)) != -1) {
switch (c) {
case 'k':
- detach_opt = PCC_DETACH_OPT_NONE;
+ detach_flags = PCC_DETACH_FL_NONE;
break;
default:
fprintf(stderr, "%s: unrecognized option '%s'\n",
fid = argv[optind++];
- rc2 = llapi_pcc_detach_fid_str(mntpath, fid, detach_opt);
+ rc2 = llapi_pcc_detach_fid_str(mntpath, fid, detach_flags);
if (rc2 < 0) {
fprintf(stderr,
"%s: cannot detach '%s' on '%s' from PCC: %s\n",
--- /dev/null
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, see
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2019, DDN Storage Corporation.
+ */
+/*
+ * lustre/utils/libhsm_scanner.c
+ *
+ * Library for scanning HSM backend fs.
+ *
+ * Author: Qian Yingjin <qian@ddn.com>
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <libcfs/util/list.h>
+#include "libhsm_scanner.h"
+
+struct hsm_scan_item {
+ struct list_head hsi_item;
+ int hsi_depth;
+ char hsi_pathname[PATH_MAX];
+};
+
+static int hsm_scan_item_alloc(struct list_head *head,
+ const char *pathname, int depth)
+{
+ struct hsm_scan_item *item;
+ int rc;
+
+ if (strlen(pathname) >= PATH_MAX) {
+ rc = -ENAMETOOLONG;
+ llapi_error(LLAPI_MSG_ERROR, rc,
+ "pathname is too long: %s\n", pathname);
+ return rc;
+ }
+
+ item = malloc(sizeof(struct hsm_scan_item));
+ if (item == NULL) {
+ rc = -ENOMEM;
+ llapi_error(LLAPI_MSG_ERROR, rc,
+ "cannot allocate hsm item for '%s'", pathname);
+ return rc;
+ }
+
+ item->hsi_depth = depth;
+ strncpy(item->hsi_pathname, pathname, sizeof(item->hsi_pathname) - 1);
+ list_add_tail(&item->hsi_item, head);
+
+ return 0;
+}
+
+int hsm_scan_handle_dir(struct hsm_scan_control *hsc, struct list_head *head,
+ struct hsm_scan_item *item)
+{
+ char fullname[PATH_MAX + NAME_MAX + 1];
+ const char *pathname = item->hsi_pathname;
+ int depth = item->hsi_depth;
+ struct dirent *ent;
+ DIR *dir;
+ int ret;
+ int rc = 0;
+
+ dir = opendir(pathname);
+ if (dir == NULL) {
+ rc = -errno;
+ llapi_error(LLAPI_MSG_ERROR, rc, "failed to opendir '%s'",
+ pathname);
+ return rc;
+ }
+
+ while ((ent = readdir(dir)) != NULL) {
+ /* skip "." and ".." */
+ if (strcmp(ent->d_name, ".") == 0 ||
+ strcmp(ent->d_name, "..") == 0)
+ continue;
+
+ llapi_printf(LLAPI_MSG_DEBUG,
+ "check file %d:'%s' under directory '%s'\n",
+ depth, ent->d_name, pathname);
+ if (depth == 0 && ent->d_type == DT_DIR &&
+ strcmp(ent->d_name, "shadow") == 0) {
+ llapi_printf(LLAPI_MSG_DEBUG,
+ "skipping check of 'shadow' directory.\n");
+ } else {
+ if (ent->d_type == DT_REG) {
+ ret = hsc->hsc_func(pathname, ent->d_name, hsc);
+ if (ret && !rc) {
+ hsc->hsc_errnum++;
+ rc = ret;
+ /* ignore error, continue to check */
+ }
+ } else if (ent->d_type == DT_DIR) {
+ if (strlen(ent->d_name) + strlen(pathname) + 1
+ >= sizeof(fullname)) {
+ rc = -ENAMETOOLONG;
+ errno = ENAMETOOLONG;
+ llapi_err_noerrno(LLAPI_MSG_ERROR,
+ "ignore too long path: %s/%s\n",
+ pathname,
+ ent->d_name);
+ hsc->hsc_errnum++;
+ continue;
+ }
+ snprintf(fullname, sizeof(fullname), "%s/%s",
+ pathname, ent->d_name);
+ rc = hsm_scan_item_alloc(head, fullname,
+ depth + 1);
+ }
+ }
+ }
+
+ if (rc)
+ llapi_error(LLAPI_MSG_ERROR, rc, "failed to handle dir '%s'",
+ pathname);
+
+ closedir(dir);
+ return rc;
+}
+
+int hsm_scan_process(struct hsm_scan_control *hsc)
+{
+ struct hsm_scan_item *item;
+ struct list_head head;
+ struct stat st;
+ int ret = 0;
+ int rc;
+
+ if (hsc->hsc_type != HSMTOOL_POSIX_V1 &&
+ hsc->hsc_type != HSMTOOL_POSIX_V2)
+ return -EOPNOTSUPP;
+
+ rc = stat(hsc->hsc_hsmpath, &st);
+ if (rc) {
+ llapi_error(LLAPI_MSG_ERROR, rc, "failed to stat '%s'",
+ hsc->hsc_hsmpath);
+ return rc;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ llapi_err_noerrno(LLAPI_MSG_ERROR,
+ "HSM root path '%s' must be a directory.",
+ hsc->hsc_hsmpath);
+ return -EINVAL;
+ }
+
+ INIT_LIST_HEAD(&head);
+ rc = hsm_scan_item_alloc(&head, hsc->hsc_hsmpath, 0);
+ if (rc)
+ return rc;
+
+ while (!list_empty(&head)) {
+ item = list_entry(head.next, struct hsm_scan_item, hsi_item);
+ list_del(&item->hsi_item);
+ ret = hsm_scan_handle_dir(hsc, &head, item);
+ if (!rc && ret)
+ rc = ret;
+ free(item);
+ }
+
+ return rc;
+}
--- /dev/null
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, see
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2019, DDN Storage Corporation.
+ */
+/*
+ * lustre/utils/libhsm_scanner.h
+ *
+ * Author: Qian Yingjin <qian@ddn.com>
+ */
+#ifndef _LIBHSM_SCANNER_H
+#define _LIBHSM_SCANNER_H
+
+#include <lustre/lustreapi.h>
+
+struct hsm_scan_control;
+
+typedef int (*hsm_scan_func_t)(const char *pname, const char *fname,
+ struct hsm_scan_control *hsc);
+
+struct hsm_scan_control {
+ enum hsmtool_type hsc_type;
+ const char *hsc_mntpath;
+ const char *hsc_hsmpath;
+ hsm_scan_func_t hsc_func;
+ int hsc_errnum;
+};
+
+int hsm_scan_process(struct hsm_scan_control *hsc);
+
+static inline bool endswith(const char *str, const char *s)
+{
+ size_t len1 = strlen(str);
+ size_t len2 = strlen(s);
+
+ if (len1 < len2)
+ return false;
+
+ return !strcmp(str + len1 - len2, s);
+}
+
+#endif /* LIBHSM_SCANNER_H */
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
+#include <lnetconfig/cyaml.h>
#include "lustreapi_internal.h"
+#include "libhsm_scanner.h"
/**
* Fetch and attach a file to readwrite PCC.
* detach PCC cache of a file by using fd.
*
* \param fd File handle.
- * \param option Detach option
+ * \param flags Detach flags.
*
* \return 0 on success, an error code otherwise.
*/
-int llapi_pcc_detach_fd(int fd, __u32 option)
+int llapi_pcc_detach_fd(int fd, __u32 flags)
{
struct lu_pcc_detach detach;
int rc;
- detach.pccd_opt = option;
+ detach.pccd_flags = flags;
rc = ioctl(fd, LL_IOC_PCC_DETACH, &detach);
/* If error, save errno value */
rc = rc ? -errno : 0;
*
* \param mntpath Fullpath to the client mount point.
* \param fid FID of the file.
- * \param option Detach option.
+ * \param flags Detach flags.
*
* \return 0 on success, an error code otherwise.
*/
int llapi_pcc_detach_fid(const char *mntpath, const struct lu_fid *fid,
- __u32 option)
+ __u32 flags)
{
int rc;
int fd;
* files.
*/
detach.pccd_fid = *fid;
- detach.pccd_opt = option;
-
+ detach.pccd_flags = flags;
rc = ioctl(fd, LL_IOC_PCC_DETACH_BY_FID, &detach);
rc = rc ? -errno : 0;
*
* \param mntpath Fullpath to the client mount point.
* \param fidstr FID string of the file.
- * \param option Detach option.
+ * \param flags Detach flags.
*
* \return 0 on success, an error code otherwise.
*/
int llapi_pcc_detach_fid_str(const char *mntpath, const char *fidstr,
- __u32 option)
+ __u32 flags)
{
int rc;
struct lu_fid fid;
return -EINVAL;
}
- rc = llapi_pcc_detach_fid(mntpath, &fid, option);
+ rc = llapi_pcc_detach_fid(mntpath, &fid, flags);
return rc;
}
* detach PCC cache of a file.
*
* \param path Fullpath to the file to operate on.
- * \param option Detach option.
+ * \param flags Detach flags.
*
* \return 0 on success, an error code otherwise.
*/
-int llapi_pcc_detach_file(const char *path, __u32 option)
+int llapi_pcc_detach_file(const char *path, __u32 flags)
{
int rc;
int fd;
return rc;
}
- rc = llapi_pcc_detach_fd(fd, option);
+ rc = llapi_pcc_detach_fd(fd, flags);
close(fd);
return rc;
}
cfs_free_param_data(&path);
return rc;
}
+
+static int llapi_pcc_scan_detach(const char *pname, const char *fname,
+ struct hsm_scan_control *hsc)
+{
+ struct lu_pcc_detach_fid detach;
+ char fullname[PATH_MAX];
+ char fidstr[FID_LEN];
+ const char *fidname;
+ bool lov_file;
+ int fd;
+ int rc;
+
+ /* It is the saved lov file when archive on HSM backend. */
+ detach.pccd_flags = PCC_DETACH_FL_UNCACHE;
+ lov_file = endswith(fname, ".lov");
+ if (lov_file) {
+ size_t len;
+
+ len = strlen(fname) - strlen(".lov");
+ if (len > sizeof(fidstr)) {
+ rc = -ENAMETOOLONG;
+ errno = ENAMETOOLONG;
+ llapi_error(LLAPI_MSG_ERROR, rc,
+ "Too long PCC-RO fname %s/%s",
+ pname, fname);
+ return rc;
+ }
+ strncpy(fidstr, fname, FID_LEN);
+ fidstr[len] = '\0';
+ detach.pccd_flags |= PCC_DETACH_FL_KNOWN_READWRITE;
+ fidname = fidstr;
+ } else {
+ fidname = fname;
+ }
+
+ rc = sscanf(fidname, SFID, RFID(&detach.pccd_fid));
+ if (rc != 3 || !fid_is_sane(&detach.pccd_fid)) {
+ llapi_err_noerrno(LLAPI_MSG_ERROR,
+ "bad FID format '%s', should be [seq:oid:ver] (e.g. "DFID")\n",
+ fidname, (unsigned long long)FID_SEQ_NORMAL,
+ 2, 0);
+ return -EINVAL;
+ }
+
+ llapi_printf(LLAPI_MSG_DEBUG, "Handle the file: %s\n", fidname);
+
+ rc = llapi_root_path_open(hsc->hsc_mntpath, &fd);
+ if (rc) {
+ llapi_error(LLAPI_MSG_ERROR, rc, "cannot get root path: %s",
+ hsc->hsc_mntpath);
+ return rc;
+ }
+
+ rc = ioctl(fd, LL_IOC_PCC_DETACH_BY_FID, &detach);
+ close(fd);
+
+ if (rc) {
+ rc = -errno;
+ llapi_printf(LLAPI_MSG_DEBUG,
+ "failed to detach file '%s': rc = %d\n",
+ fidname, rc);
+ return rc;
+ }
+
+ if (detach.pccd_flags & PCC_DETACH_FL_CACHE_REMOVED) {
+ llapi_printf(LLAPI_MSG_DEBUG,
+ "Detach and remove the PCC cached file: %s\n",
+ fidname);
+ } else {
+ snprintf(fullname, sizeof(fullname), "%s/%s", pname, fidname);
+ llapi_printf(LLAPI_MSG_DEBUG,
+ "Remove non-cached file: %s flags: %X\n",
+ fullname, detach.pccd_flags);
+ if (unlink(fullname) && errno != ENOENT) {
+ rc = -errno;
+ llapi_error(LLAPI_MSG_ERROR, rc,
+ "Unlink %s failed", fullname);
+ }
+
+ if (detach.pccd_flags & PCC_DETACH_FL_KNOWN_READWRITE) {
+ snprintf(fullname, sizeof(fullname), "%s/%s",
+ pname, fname);
+ /* Remove *.lov file */
+ unlink(fullname);
+ }
+ }
+
+ return rc;
+}
+
+static int llapi_pcc_del_internal(const char *mntpath, const char *pccpath,
+ enum hsmtool_type type,
+ enum lu_pcc_cleanup_flags flags)
+{
+ struct hsm_scan_control hsc;
+ char cmd[PATH_MAX];
+ int rc;
+
+ snprintf(cmd, sizeof(cmd), "del %s", pccpath);
+ rc = llapi_pccdev_set(mntpath, cmd);
+ if (rc < 0) {
+ llapi_error(LLAPI_MSG_ERROR, rc,
+ "failed to run '%s' on %s", cmd, mntpath);
+ return rc;
+ }
+
+ if (flags & PCC_CLEANUP_FL_KEEP_DATA)
+ return 0;
+
+ hsc.hsc_type = type;
+ hsc.hsc_mntpath = mntpath;
+ hsc.hsc_hsmpath = pccpath;
+ hsc.hsc_func = llapi_pcc_scan_detach;
+ hsc.hsc_errnum = 0;
+ rc = hsm_scan_process(&hsc);
+
+ return rc;
+}
+
+struct pcc_cmd_handler;
+
+typedef int (*pcc_handler_t)(struct cYAML *node, struct pcc_cmd_handler *pch);
+
+enum pcc_cmd_t {
+ PCC_CMD_DEL,
+ PCC_CMD_CLEAR,
+};
+
+struct pcc_cmd_handler {
+ enum pcc_cmd_t pch_cmd;
+ enum lu_pcc_type pch_type;
+ bool pch_iter_cont;
+ enum lu_pcc_cleanup_flags pch_flags;
+ const char *pch_mntpath;
+ const char *pch_pccpath;
+ pcc_handler_t pch_cb;
+ __u32 pch_id;
+};
+
+static int llapi_pcc_yaml_cb_helper(struct pcc_cmd_handler *pch)
+{
+ struct cYAML *tree = NULL, *err_rc = NULL, *pcc_node = NULL,
+ *node = NULL;
+ char pathbuf[sizeof(struct obd_uuid)];
+ glob_t path;
+ int rc;
+
+ rc = llapi_getname(pch->pch_mntpath, pathbuf, sizeof(pathbuf));
+ if (rc < 0) {
+ llapi_error(LLAPI_MSG_ERROR, rc,
+ "cannot get name for '%s'\n", pch->pch_mntpath);
+ return rc;
+ }
+
+ rc = cfs_get_param_paths(&path, "llite/%s/pcc", pathbuf);
+ if (rc != 0)
+ return -errno;
+
+ tree = cYAML_build_tree(path.gl_pathv[0], NULL, 0, &err_rc, false);
+ if (!tree) {
+ rc = -EINVAL;
+ llapi_error(LLAPI_MSG_ERROR, rc,
+ "cannot parse YAML file %s\n", path.gl_pathv[0]);
+ cYAML_build_error(rc, -1, "yaml", "from PCC yaml",
+ "can't parse", &err_rc);
+ cYAML_print_tree2file(stderr, err_rc);
+ cYAML_free_tree(err_rc);
+ goto out_free;
+ }
+
+ pcc_node = cYAML_get_object_item(tree, "pcc");
+ if (!pcc_node)
+ goto out_free;
+
+ if (!cYAML_is_sequence(pcc_node)) {
+ rc = -EINVAL;
+ llapi_error(LLAPI_MSG_ERROR, rc,
+ "bad PCC backend Array!");
+ goto out_free;
+ }
+
+ while (cYAML_get_next_seq_item(pcc_node, &node) != NULL &&
+ pch->pch_iter_cont) {
+ int ret;
+
+ ret = pch->pch_cb(node, pch);
+ if (ret && !rc)
+ rc = ret;
+ }
+
+ /* Not found the given PCC backend on the client. */
+ if (pch->pch_iter_cont && pch->pch_cmd == PCC_CMD_DEL)
+ rc = -ENOENT;
+
+out_free:
+ if (tree)
+ cYAML_free_tree(tree);
+ cfs_free_param_data(&path);
+ return rc;
+
+}
+
+static int llapi_handle_yaml_pcc_del(struct cYAML *node,
+ struct pcc_cmd_handler *pch)
+{
+ struct cYAML *pccpath, *hsmtool;
+ enum hsmtool_type type;
+
+ pccpath = cYAML_get_object_item(node, PCC_YAML_PCCPATH);
+ hsmtool = cYAML_get_object_item(node, PCC_YAML_HSMTOOL);
+
+ if (!pccpath || !pccpath->cy_valuestring ||
+ !hsmtool || !hsmtool->cy_valuestring)
+ return 0;
+
+ if (strcmp(pccpath->cy_valuestring, pch->pch_pccpath))
+ return 0;
+
+ pch->pch_iter_cont = false;
+ type = hsmtool_string2type(hsmtool->cy_valuestring);
+ return llapi_pcc_del_internal(pch->pch_mntpath, pch->pch_pccpath,
+ type, pch->pch_flags);
+}
+
+int llapi_pcc_del(const char *mntpath, const char *pccpath,
+ enum lu_pcc_cleanup_flags flags)
+{
+ struct pcc_cmd_handler pch;
+
+ pch.pch_cmd = PCC_CMD_DEL;
+ pch.pch_iter_cont = true;
+ pch.pch_mntpath = mntpath;
+ pch.pch_pccpath = pccpath;
+ pch.pch_flags = flags;
+ pch.pch_cb = llapi_handle_yaml_pcc_del;
+
+ return llapi_pcc_yaml_cb_helper(&pch);
+}
+
+
+static int llapi_handle_yaml_pcc_clear(struct cYAML *node,
+ struct pcc_cmd_handler *pch)
+{
+ struct cYAML *pccpath, *hsmtool;
+ enum hsmtool_type type;
+
+ pccpath = cYAML_get_object_item(node, PCC_YAML_PCCPATH);
+ hsmtool = cYAML_get_object_item(node, PCC_YAML_HSMTOOL);
+
+ if (!pccpath || !pccpath->cy_valuestring ||
+ !hsmtool || !hsmtool->cy_valuestring)
+ return 0;
+
+ type = hsmtool_string2type(hsmtool->cy_valuestring);
+ return llapi_pcc_del_internal(pch->pch_mntpath,
+ pccpath->cy_valuestring,
+ type, pch->pch_flags);
+}
+
+int llapi_pcc_clear(const char *mntpath, enum lu_pcc_cleanup_flags flags)
+{
+ struct pcc_cmd_handler pch;
+
+ pch.pch_cmd = PCC_CMD_CLEAR;
+ pch.pch_iter_cont = true;
+ pch.pch_mntpath = mntpath;
+ pch.pch_pccpath = NULL;
+ pch.pch_flags = flags;
+ pch.pch_cb = llapi_handle_yaml_pcc_clear;
+
+ return llapi_pcc_yaml_cb_helper(&pch);
+}
snprintf(cmd, PATH_MAX, "add %s %s", pccpath, param);
rc = llapi_pccdev_set(mntpath, cmd);
if (rc < 0)
- fprintf(stderr, "%s: failed to run '%s' on %s\n",
- jt_cmdname(argv[0]), cmd, mntpath);
+ fprintf(stderr, "%s: failed to run '%s' on '%s': %s\n",
+ jt_cmdname(argv[0]), cmd, mntpath, strerror(errno));
return rc;
}
int jt_pcc_del(int argc, char **argv)
{
+ static struct option long_opts[] = {
+ { .val = 'k', .name = "keep-data", .has_arg = no_argument },
+ { .val = 'v', .name = "verbose", .has_arg = no_argument },
+ { .name = NULL } };
+ char fsname[MAX_OBD_NAME + 1];
const char *mntpath;
const char *pccpath;
- char cmd[PATH_MAX];
+ __u32 flags = PCC_CLEANUP_FL_NONE;
+ int verbose = LLAPI_MSG_INFO;
int rc;
+ int c;
- optind = 1;
- if (argc != 3) {
- fprintf(stderr, "%s: require 2 arguments\n",
+ while ((c = getopt_long(argc, argv, "kv", long_opts, NULL)) != -1) {
+ switch (c) {
+ case 'k':
+ flags = PCC_CLEANUP_FL_KEEP_DATA;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case '?':
+ return CMD_HELP;
+ default:
+ fprintf(stderr, "%s: option '%s' unrecognized\n",
+ argv[0], argv[optind - 1]);
+ return CMD_HELP;
+ }
+ }
+ if (optind + 2 != argc) {
+ fprintf(stderr, "%s: must specify mount path and PCC path\n",
jt_cmdname(argv[0]));
return CMD_HELP;
}
mntpath = argv[optind++];
- pccpath = argv[optind++];
+ pccpath = argv[optind];
- snprintf(cmd, PATH_MAX, "del %s", pccpath);
- rc = llapi_pccdev_set(mntpath, cmd);
+ rc = llapi_search_fsname(mntpath, fsname);
+ if (rc < 0) {
+ fprintf(stderr,
+ "%s: cannot find a Lustre filesystem mounted at '%s'\n",
+ jt_cmdname(argv[0]), mntpath);
+ return rc;
+ }
+
+ /* Set llapi message level */
+ llapi_msg_set_level(verbose);
+ rc = llapi_pcc_del(mntpath, pccpath, flags);
if (rc < 0)
- fprintf(stderr, "%s: failed to run '%s' on %s\n",
- jt_cmdname(argv[0]), cmd, mntpath);
+ fprintf(stderr, "%s: failed to delete '%s' on '%s': %s\n",
+ jt_cmdname(argv[0]), pccpath, mntpath, strerror(errno));
return rc;
}
int jt_pcc_clear(int argc, char **argv)
{
+ static struct option long_opts[] = {
+ { .val = 'k', .name = "keep-data", .has_arg = no_argument },
+ { .val = 'v', .name = "verbose", .has_arg = no_argument },
+ { .name = NULL } };
+ char fsname[MAX_OBD_NAME + 1];
const char *mntpath;
+ __u32 flags = PCC_CLEANUP_FL_NONE;
+ int verbose = LLAPI_MSG_INFO;
int rc;
+ int c;
- optind = 1;
- if (argc != 2) {
- fprintf(stderr, "%s: require 1 arguments\n",
+ while ((c = getopt_long(argc, argv, "kv", long_opts, NULL)) != -1) {
+ switch (c) {
+ case 'k':
+ flags = PCC_CLEANUP_FL_KEEP_DATA;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case '?':
+ return CMD_HELP;
+ default:
+ fprintf(stderr, "%s: option '%s' unrecognized\n",
+ argv[0], argv[optind - 1]);
+ return CMD_HELP;
+ }
+ }
+ if (optind + 1 != argc) {
+ fprintf(stderr, "%s: must speficy mount path\n",
jt_cmdname(argv[0]));
return CMD_HELP;
}
mntpath = argv[optind];
- rc = llapi_pccdev_set(mntpath, "clear");
- if (rc < 0)
- fprintf(stderr, "%s: failed to run 'clear' on %s\n",
+
+ rc = llapi_search_fsname(mntpath, fsname);
+ if (rc < 0) {
+ fprintf(stderr,
+ "%s: cannot find a Lustre filesystem mounted at '%s'\n",
jt_cmdname(argv[0]), mntpath);
+ return rc;
+ }
+
+ /* Set llapi message level */
+ llapi_msg_set_level(verbose);
+ rc = llapi_pcc_clear(mntpath, flags);
+ if (rc < 0)
+ fprintf(stderr,
+ "%s: failed to remove all PCC backends on '%s': %s\n",
+ jt_cmdname(argv[0]), mntpath, strerror(errno));
return rc;
}
mntpath = argv[optind];
rc = llapi_pccdev_get(mntpath);
if (rc < 0)
- fprintf(stderr, "%s: failed to run 'pcc list' on %s\n",
- jt_cmdname(argv[0]), mntpath);
+ fprintf(stderr, "%s: failed to run 'pcc list' on '%s': %s\n",
+ jt_cmdname(argv[0]), mntpath, strerror(errno));
return rc;
}
{
BLANK_LINE();
CHECK_STRUCT(lu_pcc_detach);
- CHECK_MEMBER(lu_pcc_detach, pccd_opt);
+ CHECK_MEMBER(lu_pcc_detach, pccd_flags);
}
static void check_lu_pcc_detach_fid(void)
BLANK_LINE();
CHECK_STRUCT(lu_pcc_detach_fid);
CHECK_MEMBER(lu_pcc_detach_fid, pccd_fid);
- CHECK_MEMBER(lu_pcc_detach_fid, pccd_opt);
+ CHECK_MEMBER(lu_pcc_detach_fid, pccd_flags);
}
static void check_lu_pcc_state(void)
/* Checks for struct lu_pcc_detach */
LASSERTF((int)sizeof(struct lu_pcc_detach) == 4, "found %lld\n",
(long long)(int)sizeof(struct lu_pcc_detach));
- LASSERTF((int)offsetof(struct lu_pcc_detach, pccd_opt) == 0, "found %lld\n",
- (long long)(int)offsetof(struct lu_pcc_detach, pccd_opt));
- LASSERTF((int)sizeof(((struct lu_pcc_detach *)0)->pccd_opt) == 4, "found %lld\n",
- (long long)(int)sizeof(((struct lu_pcc_detach *)0)->pccd_opt));
+ LASSERTF((int)offsetof(struct lu_pcc_detach, pccd_flags) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lu_pcc_detach, pccd_flags));
+ LASSERTF((int)sizeof(((struct lu_pcc_detach *)0)->pccd_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_pcc_detach *)0)->pccd_flags));
/* Checks for struct lu_pcc_detach_fid */
LASSERTF((int)sizeof(struct lu_pcc_detach_fid) == 20, "found %lld\n",
(long long)(int)offsetof(struct lu_pcc_detach_fid, pccd_fid));
LASSERTF((int)sizeof(((struct lu_pcc_detach_fid *)0)->pccd_fid) == 16, "found %lld\n",
(long long)(int)sizeof(((struct lu_pcc_detach_fid *)0)->pccd_fid));
- LASSERTF((int)offsetof(struct lu_pcc_detach_fid, pccd_opt) == 16, "found %lld\n",
- (long long)(int)offsetof(struct lu_pcc_detach_fid, pccd_opt));
- LASSERTF((int)sizeof(((struct lu_pcc_detach_fid *)0)->pccd_opt) == 4, "found %lld\n",
- (long long)(int)sizeof(((struct lu_pcc_detach_fid *)0)->pccd_opt));
+ LASSERTF((int)offsetof(struct lu_pcc_detach_fid, pccd_flags) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct lu_pcc_detach_fid, pccd_flags));
+ LASSERTF((int)sizeof(((struct lu_pcc_detach_fid *)0)->pccd_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_pcc_detach_fid *)0)->pccd_flags));
/* Checks for struct lu_pcc_state */
LASSERTF((int)sizeof(struct lu_pcc_state) == 4112, "found %lld\n",