ceph: sync read inline data
[firefly-linux-kernel-4.4.55.git] / fs / ceph / addr.c
index 5d2b88e3ff0bb4784f77a5a683bfe6ac79ca7436..13413d7440d6896fc883a2625bec8f1d53458656 100644 (file)
@@ -192,17 +192,30 @@ static int readpage_nounlock(struct file *filp, struct page *page)
        struct ceph_osd_client *osdc =
                &ceph_inode_to_client(inode)->client->osdc;
        int err = 0;
+       u64 off = page_offset(page);
        u64 len = PAGE_CACHE_SIZE;
 
-       err = ceph_readpage_from_fscache(inode, page);
+       if (off >= i_size_read(inode)) {
+               zero_user_segment(page, err, PAGE_CACHE_SIZE);
+               SetPageUptodate(page);
+               return 0;
+       }
 
+       /*
+        * Uptodate inline data should have been added into page cache
+        * while getting Fcr caps.
+        */
+       if (ci->i_inline_version != CEPH_INLINE_NONE)
+               return -EINVAL;
+
+       err = ceph_readpage_from_fscache(inode, page);
        if (err == 0)
                goto out;
 
        dout("readpage inode %p file %p page %p index %lu\n",
             inode, filp, page, page->index);
        err = ceph_osdc_readpages(osdc, ceph_vino(inode), &ci->i_layout,
-                                 (u64) page_offset(page), &len,
+                                 off, &len,
                                  ci->i_truncate_seq, ci->i_truncate_size,
                                  &page, 1, 0);
        if (err == -ENOENT)
@@ -384,6 +397,9 @@ static int ceph_readpages(struct file *file, struct address_space *mapping,
        int rc = 0;
        int max = 0;
 
+       if (ceph_inode(inode)->i_inline_version != CEPH_INLINE_NONE)
+               return -EINVAL;
+
        rc = ceph_readpages_from_fscache(mapping->host, mapping, page_list,
                                         &nr_pages);
 
@@ -1219,8 +1235,8 @@ static int ceph_filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
                want = CEPH_CAP_FILE_CACHE;
        while (1) {
                got = 0;
-               ret = ceph_get_caps(ci, CEPH_CAP_FILE_RD, want, -1,
-                                   &got, &pinned_page);
+               ret = ceph_get_caps(ci, CEPH_CAP_FILE_RD, want,
+                                   -1, &got, &pinned_page);
                if (ret == 0)
                        break;
                if (ret != -ERESTARTSYS) {
@@ -1231,7 +1247,11 @@ static int ceph_filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
        dout("filemap_fault %p %llu~%zd got cap refs on %s\n",
             inode, off, (size_t)PAGE_CACHE_SIZE, ceph_cap_string(got));
 
-       ret = filemap_fault(vma, vmf);
+       if ((got & (CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_LAZYIO)) ||
+           ci->i_inline_version == CEPH_INLINE_NONE)
+               ret = filemap_fault(vma, vmf);
+       else
+               ret = -EAGAIN;
 
        dout("filemap_fault %p %llu~%zd dropping cap refs on %s ret %d\n",
             inode, off, (size_t)PAGE_CACHE_SIZE, ceph_cap_string(got), ret);
@@ -1239,6 +1259,42 @@ static int ceph_filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
                page_cache_release(pinned_page);
        ceph_put_cap_refs(ci, got);
 
+       if (ret != -EAGAIN)
+               return ret;
+
+       /* read inline data */
+       if (off >= PAGE_CACHE_SIZE) {
+               /* does not support inline data > PAGE_SIZE */
+               ret = VM_FAULT_SIGBUS;
+       } else {
+               int ret1;
+               struct address_space *mapping = inode->i_mapping;
+               struct page *page = find_or_create_page(mapping, 0,
+                                               mapping_gfp_mask(mapping) &
+                                               ~__GFP_FS);
+               if (!page) {
+                       ret = VM_FAULT_OOM;
+                       goto out;
+               }
+               ret1 = __ceph_do_getattr(inode, page,
+                                        CEPH_STAT_CAP_INLINE_DATA, true);
+               if (ret1 < 0 || off >= i_size_read(inode)) {
+                       unlock_page(page);
+                       page_cache_release(page);
+                       ret = VM_FAULT_SIGBUS;
+                       goto out;
+               }
+               if (ret1 < PAGE_CACHE_SIZE)
+                       zero_user_segment(page, ret1, PAGE_CACHE_SIZE);
+               else
+                       flush_dcache_page(page);
+               SetPageUptodate(page);
+               vmf->page = page;
+               ret = VM_FAULT_MAJOR | VM_FAULT_LOCKED;
+       }
+out:
+       dout("filemap_fault %p %llu~%zd read inline data ret %d\n",
+            inode, off, (size_t)PAGE_CACHE_SIZE, ret);
        return ret;
 }