]> Pileus Git - ~andy/linux/blobdiff - fs/ceph/addr.c
Linux 3.14
[~andy/linux] / fs / ceph / addr.c
index ec3ba43b9faae73fba6d6352da9515a812ec9f36..b53278c9fd9718509e96d91d3ea1027581714214 100644 (file)
@@ -209,6 +209,7 @@ static int readpage_nounlock(struct file *filp, struct page *page)
                err = 0;
        if (err < 0) {
                SetPageError(page);
+               ceph_fscache_readpage_cancel(inode, page);
                goto out;
        } else {
                if (err < PAGE_CACHE_SIZE) {
@@ -256,6 +257,8 @@ static void finish_read(struct ceph_osd_request *req, struct ceph_msg *msg)
        for (i = 0; i < num_pages; i++) {
                struct page *page = osd_data->pages[i];
 
+               if (rc < 0)
+                       goto unlock;
                if (bytes < (int)PAGE_CACHE_SIZE) {
                        /* zero (remainder of) page */
                        int s = bytes < 0 ? 0 : bytes;
@@ -266,6 +269,7 @@ static void finish_read(struct ceph_osd_request *req, struct ceph_msg *msg)
                flush_dcache_page(page);
                SetPageUptodate(page);
                ceph_readpage_to_fscache(inode, page);
+unlock:
                unlock_page(page);
                page_cache_release(page);
                bytes -= PAGE_CACHE_SIZE;
@@ -1207,6 +1211,41 @@ const struct address_space_operations ceph_aops = {
 /*
  * vm ops
  */
+static int ceph_filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+       struct inode *inode = file_inode(vma->vm_file);
+       struct ceph_inode_info *ci = ceph_inode(inode);
+       struct ceph_file_info *fi = vma->vm_file->private_data;
+       loff_t off = vmf->pgoff << PAGE_CACHE_SHIFT;
+       int want, got, ret;
+
+       dout("filemap_fault %p %llx.%llx %llu~%zd trying to get caps\n",
+            inode, ceph_vinop(inode), off, (size_t)PAGE_CACHE_SIZE);
+       if (fi->fmode & CEPH_FILE_MODE_LAZY)
+               want = CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_LAZYIO;
+       else
+               want = CEPH_CAP_FILE_CACHE;
+       while (1) {
+               got = 0;
+               ret = ceph_get_caps(ci, CEPH_CAP_FILE_RD, want, &got, -1);
+               if (ret == 0)
+                       break;
+               if (ret != -ERESTARTSYS) {
+                       WARN_ON(1);
+                       return VM_FAULT_SIGBUS;
+               }
+       }
+       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);
+
+       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);
+       ceph_put_cap_refs(ci, got);
+
+       return ret;
+}
 
 /*
  * Reuse write_begin here for simplicity.
@@ -1214,23 +1253,41 @@ const struct address_space_operations ceph_aops = {
 static int ceph_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
 {
        struct inode *inode = file_inode(vma->vm_file);
-       struct page *page = vmf->page;
+       struct ceph_inode_info *ci = ceph_inode(inode);
+       struct ceph_file_info *fi = vma->vm_file->private_data;
        struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
+       struct page *page = vmf->page;
        loff_t off = page_offset(page);
-       loff_t size, len;
-       int ret;
-
-       /* Update time before taking page lock */
-       file_update_time(vma->vm_file);
+       loff_t size = i_size_read(inode);
+       size_t len;
+       int want, got, ret;
 
-       size = i_size_read(inode);
        if (off + PAGE_CACHE_SIZE <= size)
                len = PAGE_CACHE_SIZE;
        else
                len = size & ~PAGE_CACHE_MASK;
 
-       dout("page_mkwrite %p %llu~%llu page %p idx %lu\n", inode,
-            off, len, page, page->index);
+       dout("page_mkwrite %p %llx.%llx %llu~%zd getting caps i_size %llu\n",
+            inode, ceph_vinop(inode), off, len, size);
+       if (fi->fmode & CEPH_FILE_MODE_LAZY)
+               want = CEPH_CAP_FILE_BUFFER | CEPH_CAP_FILE_LAZYIO;
+       else
+               want = CEPH_CAP_FILE_BUFFER;
+       while (1) {
+               got = 0;
+               ret = ceph_get_caps(ci, CEPH_CAP_FILE_WR, want, &got, off + len);
+               if (ret == 0)
+                       break;
+               if (ret != -ERESTARTSYS) {
+                       WARN_ON(1);
+                       return VM_FAULT_SIGBUS;
+               }
+       }
+       dout("page_mkwrite %p %llu~%zd got cap refs on %s\n",
+            inode, off, len, ceph_cap_string(got));
+
+       /* Update time before taking page lock */
+       file_update_time(vma->vm_file);
 
        lock_page(page);
 
@@ -1252,14 +1309,26 @@ static int ceph_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
                        ret = VM_FAULT_SIGBUS;
        }
 out:
-       dout("page_mkwrite %p %llu~%llu = %d\n", inode, off, len, ret);
-       if (ret != VM_FAULT_LOCKED)
+       if (ret != VM_FAULT_LOCKED) {
                unlock_page(page);
+       } else {
+               int dirty;
+               spin_lock(&ci->i_ceph_lock);
+               dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR);
+               spin_unlock(&ci->i_ceph_lock);
+               if (dirty)
+                       __mark_inode_dirty(inode, dirty);
+       }
+
+       dout("page_mkwrite %p %llu~%zd dropping cap refs on %s ret %d\n",
+            inode, off, len, ceph_cap_string(got), ret);
+       ceph_put_cap_refs(ci, got);
+
        return ret;
 }
 
 static struct vm_operations_struct ceph_vmops = {
-       .fault          = filemap_fault,
+       .fault          = ceph_filemap_fault,
        .page_mkwrite   = ceph_page_mkwrite,
        .remap_pages    = generic_file_remap_pages,
 };