int len = PAGE_CACHE_SIZE;
loff_t i_size;
int err = 0;
+ int was_dirty;
if (!page->mapping || !page->mapping->host)
return -EFAULT;
inode, page, page->index, page_off, len);
page_cache_get(page);
+ was_dirty = PageDirty(page);
set_page_writeback(page);
kmap(page);
err = ceph_osdc_writepages(osdc, ceph_ino(inode), &ci->i_layout,
page_off, len, &page, 1);
kunmap(page);
- if (err >= 0)
+ if (err >= 0) {
+ if (was_dirty)
+ ceph_put_wrbuffer_cap_refs(ci, 1);
SetPageUptodate(page);
- else
+ } else
redirty_page_for_writepage(wbc, page); /* is this right?? */
unlock_page(page);
end_page_writeback(page);
want);
for (i = 0; i < pvec_pages && locked_pages < max_pages; i++) {
page = pvec.pages[i];
+
if (locked_pages == 0)
lock_page(page);
else if (TestSetPageLocked(page))
break;
- if (unlikely(page->mapping != mapping)) {
+ /* only dirty pages, or wrbuffer accounting breaks! */
+ if (unlikely(!PageDirty(page)) ||
+ unlikely(page->mapping != mapping)) {
unlock_page(page);
break;
}
}
if (wbc->sync_mode != WB_SYNC_NONE)
wait_on_page_writeback(page);
+
if (PageWriteback(page) ||
!clear_page_dirty_for_io(page)) {
unlock_page(page);
/* ok */
set_page_writeback(page);
+
if (page_offset(page) >= i_size_read(inode)) {
done = 1;
unlock_page(page);
end_page_writeback(page);
break;
}
-
+
dout(50, "writepages locked page %p index %lu\n",
page, page->index);
+
kmap(page);
if (pages)
pages[locked_pages] = page;
loff_t offset = pagep[0]->index << PAGE_CACHE_SHIFT;
loff_t len = min(i_size_read(inode) - offset,
(loff_t)locked_pages << PAGE_CACHE_SHIFT);
+ unsigned wrote;
dout(10, "writepages got %d pages at %llu~%llu\n",
locked_pages, offset, len);
rc = ceph_osdc_writepages(&client->osdc,
offset, len,
pagep,
locked_pages);
- dout(20, "writepages rc %d\n", rc);
+ if (rc >= 0)
+ wrote = (rc + (offset & ~PAGE_CACHE_MASK)
+ + ~PAGE_CACHE_MASK)
+ >> PAGE_CACHE_SHIFT;
+ else
+ wrote = 0;
+ dout(20, "writepages rc %d wrote %d\n", rc, wrote);
/* unmap+unlock pages */
- if (rc >= 0)
- rc += offset & ~PAGE_CACHE_MASK;
for (i = 0; i < locked_pages; i++) {
page = pagep[i];
- if (rc > (i << PAGE_CACHE_SHIFT))
+ if (i < wrote)
SetPageUptodate(page);
else if (rc < 0)
- SetPageError(page);
+ redirty_page_for_writepage(wbc, page);
kunmap(page);
dout(50, "unlocking %d %p\n", i, page);
unlock_page(page);
end_page_writeback(page);
}
+ ceph_put_wrbuffer_cap_refs(ci, wrote);
/* continue? */
index = next;
if (!PageUptodate(page))
SetPageUptodate(page);
+ if (!PageDirty(page))
+ ceph_take_cap_refs(ceph_inode(inode), CEPH_CAP_WRBUFFER);
+ else
+ dout(10, "page %p already dirty\n", page);
set_page_dirty(page);
unlock_page(page);
* page accounting
*/
-static int ceph_set_page_dirty(struct page *page)
-{
- struct ceph_inode_info *ci = ceph_inode(page->mapping->host);
- spin_lock(&ci->vfs_inode.i_lock);
- dout(10, "set_page_dirty %p : %d -> %d \n", page,
- ci->i_nr_dirty_pages, ci->i_nr_dirty_pages + 1);
- ci->i_nr_dirty_pages++;
- spin_lock(&ci->vfs_inode.i_lock);
- return 0;
-}
-
+/*
static int ceph_releasepage(struct page *page, gfp_t gfpmask)
{
struct ceph_inode_info *ci = ceph_inode(page->mapping->host);
ceph_check_caps_wanted(ci, gfpmask);
return 0;
}
-
+*/
const struct address_space_operations ceph_aops = {
.readpage = ceph_readpage,
.writepage = ceph_writepage,
.writepages = ceph_writepages,
.write_begin = ceph_write_begin,
- .write_end = simple_write_end, /*ceph_write_end,*/
+ .write_end = ceph_write_end,
// .set_page_dirty = ceph_set_page_dirty,
- .releasepage = ceph_releasepage,
+ //.releasepage = ceph_releasepage,
};
ci->i_wrbuffer_ref++;
}
+void ceph_take_cap_refs(struct ceph_inode_info *ci, int got)
+{
+ dout(20, "take_cap_refs on %p taking %d\n", &ci->vfs_inode, got);
+ spin_lock(&ci->vfs_inode.i_lock);
+ __take_cap_refs(ci, got);
+ spin_unlock(&ci->vfs_inode.i_lock);
+}
+
int ceph_get_cap_refs(struct ceph_inode_info *ci, int need, int want, int *got)
{
int ret = 0;
need, want);
spin_lock(&ci->vfs_inode.i_lock);
have = __ceph_caps_issued(ci);
- dout(10, "get_cap_refs have %d\n", have);
+ dout(20, "get_cap_refs have %d\n", have);
if ((have & need) == need) {
*got = need | (have & want);
__take_cap_refs(ci, *got);
if (--ci->i_wr_ref == 0)
last++;
if (had & CEPH_CAP_WRBUFFER)
- if (--ci->i_wrbuffer_ref)
+ if (--ci->i_wrbuffer_ref == 0)
last++;
spin_unlock(&ci->vfs_inode.i_lock);
+ dout(10, "put_cap_refs on %p had %d %s\n", &ci->vfs_inode, had,
+ last ? "last":"");
+
if (last)
ceph_check_caps_wanted(ci, GFP_KERNEL);
}
+void ceph_put_wrbuffer_cap_refs(struct ceph_inode_info *ci, int nr)
+{
+ int last = 0;
+
+ spin_lock(&ci->vfs_inode.i_lock);
+ ci->i_wrbuffer_ref -= nr;
+ if (ci->i_wrbuffer_ref == 0)
+ last++;
+ BUG_ON(ci->i_wrbuffer_ref < 0);
+ spin_unlock(&ci->vfs_inode.i_lock);
+
+ dout(10, "put_wrbuffer_cap_refs on %p nr %d %s\n", &ci->vfs_inode, nr,
+ last ? "last":"");
+
+ if (last)
+ ceph_check_caps_wanted(ci, GFP_KERNEL);
+}
/*
/* held references to caps */
int i_rd_ref, i_rdcache_ref, i_wr_ref, i_wrbuffer_ref;
- int i_nr_pages, i_nr_dirty_pages; // hrm!
-
unsigned long i_hashval;
struct inode vfs_inode; /* at end */
struct ceph_mds_file_caps *grant,
struct ceph_mds_session *session);
extern int ceph_get_cap_refs(struct ceph_inode_info *ci, int need, int want, int *got);
+extern void ceph_take_cap_refs(struct ceph_inode_info *ci, int got);
extern void ceph_put_cap_refs(struct ceph_inode_info *ci, int had);
+extern void ceph_put_wrbuffer_cap_refs(struct ceph_inode_info *ci, int nr);
extern void ceph_check_caps_wanted(struct ceph_inode_info *ci, gfp_t gfpmask);
extern void ceph_get_mode(struct ceph_inode_info *ci, int mode);
extern void ceph_put_mode(struct ceph_inode_info *ci, int mode);