+#include <linux/backing-dev.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/writeback.h> /* generic_writepages */
#include <linux/pagevec.h>
+#include <linux/task_io_accounting_ops.h>
int ceph_debug_addr = -1;
#define DOUT_VAR ceph_debug_addr
if (i < wrote)
SetPageUptodate(page);
else if (rc < 0) {
- cleaned--;
dout(20, "%p redirtying page %p\n",
inode, page);
redirty_page_for_writepage(wbc, page);
if (!PageUptodate(page))
SetPageUptodate(page);
- if (!PageDirty(page)) {
- dout(20, "%p dirtying page %p\n", inode, page);
- ceph_take_cap_refs(ceph_inode(inode), CEPH_CAP_WRBUFFER);
- } else
- dout(20, "%p page %p already dirty\n", inode, page);
set_page_dirty(page);
unlock_page(page);
}
+static int ceph_set_page_dirty(struct page *page)
+{
+ struct address_space *mapping = page->mapping;
+ struct ceph_inode_info *ci;
+
+ if (unlikely(!mapping))
+ return !TestSetPageDirty(page);
+
+ if (TestSetPageDirty(page)) {
+ dout(20, "set_page_dirty %p -- already dirty\n", page);
+ return 0;
+ }
+
+ write_lock_irq(&mapping->tree_lock);
+ if (page->mapping) { /* Race with truncate? */
+ WARN_ON_ONCE(!PageUptodate(page));
+
+ if (mapping_cap_account_dirty(mapping)) {
+ __inc_zone_page_state(page, NR_FILE_DIRTY);
+ __inc_bdi_stat(mapping->backing_dev_info,
+ BDI_RECLAIMABLE);
+ task_io_account_write(PAGE_CACHE_SIZE);
+ }
+ radix_tree_tag_set(&mapping->page_tree,
+ page_index(page), PAGECACHE_TAG_DIRTY);
+
+ ci = ceph_inode(mapping->host);
+ atomic_inc(&ci->i_wrbuffer_ref);
+ dout(20, "set_page_dirty %p %p %d -> %d (?)\n", page,
+ &ci->vfs_inode,
+ atomic_read(&ci->i_wrbuffer_ref)-1,
+ atomic_read(&ci->i_wrbuffer_ref));
+ }
+ write_unlock_irq(&mapping->tree_lock);
+ __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
+
+ return 1;
+}
+
const struct address_space_operations ceph_aops = {
.readpage = ceph_readpage,
.writepages = ceph_writepages,
.write_begin = ceph_write_begin,
.write_end = ceph_write_end,
+ .set_page_dirty = ceph_set_page_dirty,
};
if (got & CEPH_CAP_WR)
ci->i_wr_ref++;
if (got & CEPH_CAP_WRBUFFER) {
- ci->i_wrbuffer_ref++;
- dout(30, "__take_cap_refs %p wrbuffer %d -> %d\n",
- &ci->vfs_inode, ci->i_wrbuffer_ref-1, ci->i_wrbuffer_ref);
+ atomic_inc(&ci->i_wrbuffer_ref);
+ dout(30, "__take_cap_refs %p wrbuffer %d -> %d (?)\n",
+ &ci->vfs_inode, atomic_read(&ci->i_wrbuffer_ref)-1,
+ atomic_read(&ci->i_wrbuffer_ref));
}
}
if (--ci->i_wr_ref == 0)
last++;
if (had & CEPH_CAP_WRBUFFER) {
- if (--ci->i_wrbuffer_ref == 0)
+ if (atomic_dec_and_test(&ci->i_wrbuffer_ref))
last++;
- dout(30, "put_cap_refs %p wrbuffer %d -> %d\n",
- &ci->vfs_inode, ci->i_wrbuffer_ref+1,ci->i_wrbuffer_ref);
+ dout(30, "put_cap_refs %p wrbuffer %d -> %d (?)\n",
+ &ci->vfs_inode, atomic_read(&ci->i_wrbuffer_ref)+1,
+ atomic_read(&ci->i_wrbuffer_ref));
}
spin_unlock(&ci->vfs_inode.i_lock);
void ceph_put_wrbuffer_cap_refs(struct ceph_inode_info *ci, int nr)
{
int last = 0;
+ int v;
spin_lock(&ci->vfs_inode.i_lock);
- ci->i_wrbuffer_ref -= nr;
- last = ci->i_wrbuffer_ref;
+ last = atomic_sub_and_test(nr, &ci->i_wrbuffer_ref);
+ v = atomic_read(&ci->i_wrbuffer_ref);
spin_unlock(&ci->vfs_inode.i_lock);
- dout(30, "put_wrbuffer_cap_refs on %p %d -> %d%s\n",
- &ci->vfs_inode, last+nr, last, last == 0 ? " LAST":"");
- BUG_ON(ci->i_wrbuffer_ref < 0);
+ dout(30, "put_wrbuffer_cap_refs on %p %d -> %d (?)%s\n",
+ &ci->vfs_inode, v+nr, v, last == 0 ? " LAST":"");
+ BUG_ON(v < 0);
if (last == 0)
ceph_check_caps(ci, 0);
ci->i_requested_max_size = 0;
ci->i_rd_ref = ci->i_rdcache_ref = 0;
- ci->i_wr_ref = ci->i_wrbuffer_ref = 0;
+ ci->i_wr_ref = 0;
+ atomic_set(&ci->i_wrbuffer_ref, 0);
ci->i_hold_caps_until = 0;
INIT_LIST_HEAD(&ci->i_cap_delay_list);
struct timespec i_old_atime;
/* held references to caps */
- int i_rd_ref, i_rdcache_ref, i_wr_ref, i_wrbuffer_ref;
+ int i_rd_ref, i_rdcache_ref, i_wr_ref;
+ atomic_t i_wrbuffer_ref;
unsigned long i_hashval;
used |= CEPH_CAP_RDCACHE;
if (ci->i_wr_ref)
used |= CEPH_CAP_WR;
- if (ci->i_wrbuffer_ref)
+ if (atomic_read(&ci->i_wrbuffer_ref))
used |= CEPH_CAP_WRBUFFER;
return used;
}