return rc;
if (id > 0)
cmd->u.pccc_add.pccc_flags |= PCC_DATASET_ROPCC;
+ } else if (strcmp(key, "mmap_conv") == 0) {
+ rc = kstrtoul(val, 10, &id);
+ if (rc)
+ return rc;
+ if (id > 0)
+ cmd->u.pccc_add.pccc_flags |= PCC_DATASET_MMAP_CONV;
+ else if (id == 0)
+ cmd->u.pccc_add.pccc_flags &= ~PCC_DATASET_MMAP_CONV;
} else {
return -EINVAL;
}
switch (cmd->pccc_cmd) {
case PCC_ADD_DATASET:
- /* Enable auto attach by default */
- cmd->u.pccc_add.pccc_flags |= PCC_DATASET_AUTO_ATTACH;
+ /* Enable auto attach and mmap pagecache convert by default */
+ cmd->u.pccc_add.pccc_flags |= PCC_DATASET_AUTO_ATTACH |
+ PCC_DATASET_MMAP_CONV;
break;
case PCC_DEL_DATASET:
case PCC_CLEAR_ALL:
PFID(&ll_i2info(inode)->lli_fid), rc);
/* ignore the error during auto PCC-RO attach. */
rc = 0;
+ } else {
+ CDEBUG(D_CACHE,
+ "PCC-RO attach %pd "DFID" with size %llu\n",
+ dentry, PFID(ll_inode2fid(inode)),
+ i_size_read(inode));
}
}
* Thus, It needs a mechanism to forbid users to access the PCC copy
* directly from the user space and the PCC copy can only be accessed
* from Lustre PCC hook.
- * Maybe set the file operations of the inode (@i_fop) with empty file
- * operations is a solution.
+ * One solution is to use flock() to lock the PCC copy when the file
+ * is once attached into PCC and unlock it when the file is detached
+ * from PCC. By this way, the PCC copy is blocking on access from user
+ * space directly when it is valid cached on PCC.
*/
if (pcc_inode_has_layout(pcci))
mapping->host = inode;
pcc_inode->i_mapping = &pcc_inode->i_data;
- CDEBUG(D_CACHE, "Mapping reset for inode %p fid="DFID" mapping %p\n",
+ CDEBUG(D_CACHE, "Reset mapping for inode %p fid="DFID" mapping %p\n",
inode, PFID(ll_inode2fid(inode)), inode->i_mapping);
}
pcc_inode_unlock(inode);
}
+static int pcc_mmap_pages_convert(struct inode *inode,
+ struct inode *pcc_inode)
+{
+ struct pagevec pvec;
+ pgoff_t index = 0;
+ int nr_pages;
+ int rc = 0;
+
+ ll_pagevec_init(&pvec, 0);
+ for ( ; ; ) {
+ struct page *page;
+ int i;
+
+#ifdef HAVE_PAGEVEC_LOOKUP_THREE_PARAM
+ nr_pages = pagevec_lookup(&pvec, pcc_inode->i_mapping, &index);
+#else
+ nr_pages = pagevec_lookup(&pvec, pcc_inode->i_mapping, index,
+ PAGEVEC_SIZE);
+#endif
+ if (nr_pages <= 0)
+ break;
+
+ for (i = 0; i < nr_pages; i++) {
+ page = pvec.pages[i];
+ lock_page(page);
+ wait_on_page_writeback(page);
+
+ /*
+ * FIXME: Special handling for shadow or DAX entries.
+ * i.e. the PCC backend FS is using DAX access
+ * (ext4-dax) for performance reason on the NVMe
+ * hardware.
+ */
+ /* Remove the page from the mapping of the PCC copy. */
+ delete_from_page_cache(page);
+ /* Add the page into the mapping of the Lustre file. */
+ rc = add_to_page_cache_locked(page, inode->i_mapping,
+ page->index, GFP_KERNEL);
+ if (rc) {
+ unlock_page(page);
+ pagevec_release(&pvec);
+ return rc;
+ }
+
+ unlock_page(page);
+ }
+
+ index = page->index + 1;
+ pagevec_release(&pvec);
+ cond_resched();
+ }
+
+ return rc;
+}
+
+static int pcc_mmap_mapping_set(struct inode *inode, struct inode *pcc_inode)
+{
+ struct address_space *mapping = inode->i_mapping;
+ struct pcc_inode *pcci = ll_i2pcci(inode);
+ int rc;
+
+ ENTRY;
+
+ if (pcc_inode->i_mapping == mapping) {
+ LASSERT(mapping->host == pcc_inode);
+ LASSERT(mapping->a_ops == pcc_inode->i_mapping->a_ops);
+ RETURN(0);
+ }
+
+ if (pcc_inode->i_mapping != &pcc_inode->i_data)
+ RETURN(-EBUSY);
+ /*
+ * Write out all dirty pages and drop all pagecaches before switch the
+ * mapping from the PCC copy to the Lustre file for PCC mmap().
+ */
+
+ rc = filemap_write_and_wait_range(mapping, 0, LUSTRE_EOF);
+ if (rc)
+ return rc;
+
+ truncate_inode_pages(mapping, 0);
+
+ /* Wait all active I/Os on the PCC copy finished. */
+ wait_event_idle(pcci->pcci_waitq,
+ atomic_read(&pcci->pcci_active_ios) == 0);
+
+ rc = filemap_write_and_wait_range(pcc_inode->i_mapping, 0, LUSTRE_EOF);
+ if (rc)
+ return rc;
+
+ if (ll_i2info(inode)->lli_pcc_dsflags & PCC_DATASET_MMAP_CONV) {
+ /*
+ * Move and convert all pagecache on the mapping of the PCC copy
+ * to the Lustre file.
+ */
+ rc = pcc_mmap_pages_convert(inode, pcc_inode);
+ if (rc)
+ return rc;
+ } else {
+ /* Drop all pagecache on the PCC copy directly. */
+ truncate_inode_pages(pcc_inode->i_mapping, 0);
+ }
+
+ mapping->a_ops = pcc_inode->i_mapping->a_ops;
+ mapping->host = pcc_inode;
+ pcc_inode->i_mapping = mapping;
+
+ RETURN(rc);
+}
+
int pcc_file_mmap(struct file *file, struct vm_area_struct *vma,
bool *cached)
{
pcc_inode_lock(inode);
pcci = ll_i2pcci(inode);
if (pcci && pcc_inode_has_layout(pcci)) {
- struct address_space *mapping = inode->i_mapping;
struct inode *pcc_inode = file_inode(pcc_file);
struct pcc_vma *pccv;
LASSERT(atomic_read(&pcci->pcci_refcount) > 1);
*cached = true;
- if (pcc_inode->i_mapping == &pcc_inode->i_data) {
- /*
- * Write out all dirty pages and drop all pagecaches
- * before switch the mapping from the PCC copy to the
- * Lustre file for PCC mmap().
- */
- rc = filemap_write_and_wait_range(inode->i_mapping, 0,
- LUSTRE_EOF);
- if (rc)
- GOTO(out, rc);
-
- truncate_inode_pages(mapping, 0);
- rc = filemap_write_and_wait_range(pcc_inode->i_mapping,
- 0, LUSTRE_EOF);
- if (rc)
- GOTO(out, rc);
-
- truncate_inode_pages(pcc_inode->i_mapping, 0);
- mapping->a_ops = pcc_inode->i_mapping->a_ops;
- mapping->host = pcc_inode;
- pcc_inode->i_mapping = mapping;
- } else if (pcc_inode->i_mapping != mapping) {
- GOTO(out, rc = -EBUSY);
- }
+ rc = pcc_mmap_mapping_set(inode, pcc_inode);
+ if (rc)
+ GOTO(out, rc);
OBD_ALLOC_PTR(pccv);
if (pccv == NULL)