size_t len;
        int want, got, ret;
 
+       if (ci->i_inline_version != CEPH_INLINE_NONE) {
+               struct page *locked_page = NULL;
+               if (off == 0) {
+                       lock_page(page);
+                       locked_page = page;
+               }
+               ret = ceph_uninline_data(vma->vm_file, locked_page);
+               if (locked_page)
+                       unlock_page(locked_page);
+               if (ret < 0)
+                       return VM_FAULT_SIGBUS;
+       }
+
        if (off + PAGE_CACHE_SIZE <= size)
                len = PAGE_CACHE_SIZE;
        else
                        ret = VM_FAULT_SIGBUS;
        }
 out:
-       if (ret != VM_FAULT_LOCKED) {
+       if (ret != VM_FAULT_LOCKED)
                unlock_page(page);
-       } else {
+       if (ret == VM_FAULT_LOCKED ||
+           ci->i_inline_version != CEPH_INLINE_NONE) {
                int dirty;
                spin_lock(&ci->i_ceph_lock);
+               ci->i_inline_version = CEPH_INLINE_NONE;
                dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR);
                spin_unlock(&ci->i_ceph_lock);
                if (dirty)
        }
 }
 
+int ceph_uninline_data(struct file *filp, struct page *locked_page)
+{
+       struct inode *inode = file_inode(filp);
+       struct ceph_inode_info *ci = ceph_inode(inode);
+       struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
+       struct ceph_osd_request *req;
+       struct page *page = NULL;
+       u64 len, inline_version;
+       int err = 0;
+       bool from_pagecache = false;
+
+       spin_lock(&ci->i_ceph_lock);
+       inline_version = ci->i_inline_version;
+       spin_unlock(&ci->i_ceph_lock);
+
+       dout("uninline_data %p %llx.%llx inline_version %llu\n",
+            inode, ceph_vinop(inode), inline_version);
+
+       if (inline_version == 1 || /* initial version, no data */
+           inline_version == CEPH_INLINE_NONE)
+               goto out;
+
+       if (locked_page) {
+               page = locked_page;
+               WARN_ON(!PageUptodate(page));
+       } else if (ceph_caps_issued(ci) &
+                  (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) {
+               page = find_get_page(inode->i_mapping, 0);
+               if (page) {
+                       if (PageUptodate(page)) {
+                               from_pagecache = true;
+                               lock_page(page);
+                       } else {
+                               page_cache_release(page);
+                               page = NULL;
+                       }
+               }
+       }
+
+       if (page) {
+               len = i_size_read(inode);
+               if (len > PAGE_CACHE_SIZE)
+                       len = PAGE_CACHE_SIZE;
+       } else {
+               page = __page_cache_alloc(GFP_NOFS);
+               if (!page) {
+                       err = -ENOMEM;
+                       goto out;
+               }
+               err = __ceph_do_getattr(inode, page,
+                                       CEPH_STAT_CAP_INLINE_DATA, true);
+               if (err < 0) {
+                       /* no inline data */
+                       if (err == -ENODATA)
+                               err = 0;
+                       goto out;
+               }
+               len = err;
+       }
+
+       req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout,
+                                   ceph_vino(inode), 0, &len, 0, 1,
+                                   CEPH_OSD_OP_CREATE,
+                                   CEPH_OSD_FLAG_ONDISK | CEPH_OSD_FLAG_WRITE,
+                                   ci->i_snap_realm->cached_context,
+                                   0, 0, false);
+       if (IS_ERR(req)) {
+               err = PTR_ERR(req);
+               goto out;
+       }
+
+       ceph_osdc_build_request(req, 0, NULL, CEPH_NOSNAP, &inode->i_mtime);
+       err = ceph_osdc_start_request(&fsc->client->osdc, req, false);
+       if (!err)
+               err = ceph_osdc_wait_request(&fsc->client->osdc, req);
+       ceph_osdc_put_request(req);
+       if (err < 0)
+               goto out;
+
+       req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout,
+                                   ceph_vino(inode), 0, &len, 1, 3,
+                                   CEPH_OSD_OP_WRITE,
+                                   CEPH_OSD_FLAG_ONDISK | CEPH_OSD_FLAG_WRITE,
+                                   ci->i_snap_realm->cached_context,
+                                   ci->i_truncate_seq, ci->i_truncate_size,
+                                   false);
+       if (IS_ERR(req)) {
+               err = PTR_ERR(req);
+               goto out;
+       }
+
+       osd_req_op_extent_osd_data_pages(req, 1, &page, len, 0, false, false);
+
+       err = osd_req_op_xattr_init(req, 0, CEPH_OSD_OP_CMPXATTR,
+                                   "inline_version", &inline_version,
+                                   sizeof(inline_version),
+                                   CEPH_OSD_CMPXATTR_OP_GT,
+                                   CEPH_OSD_CMPXATTR_MODE_U64);
+       if (err)
+               goto out_put;
+
+       err = osd_req_op_xattr_init(req, 2, CEPH_OSD_OP_SETXATTR,
+                                   "inline_version", &inline_version,
+                                   sizeof(inline_version), 0, 0);
+       if (err)
+               goto out_put;
+
+       ceph_osdc_build_request(req, 0, NULL, CEPH_NOSNAP, &inode->i_mtime);
+       err = ceph_osdc_start_request(&fsc->client->osdc, req, false);
+       if (!err)
+               err = ceph_osdc_wait_request(&fsc->client->osdc, req);
+out_put:
+       ceph_osdc_put_request(req);
+       if (err == -ECANCELED)
+               err = 0;
+out:
+       if (page && page != locked_page) {
+               if (from_pagecache) {
+                       unlock_page(page);
+                       page_cache_release(page);
+               } else
+                       __free_pages(page, 0);
+       }
+
+       dout("uninline_data %p %llx.%llx inline_version %llu = %d\n",
+            inode, ceph_vinop(inode), inline_version, err);
+       return err;
+}
+
 static struct vm_operations_struct ceph_vmops = {
        .fault          = ceph_filemap_fault,
        .page_mkwrite   = ceph_page_mkwrite,