[RFC v2 73/83] Dax: Add iomap operations.

2018-03-10 Thread Andiry Xu
From: Andiry Xu 

The key of iomap is dax_get_blocks(). It first takes the read lock
and lookup the block; if the block is missing, it takes write lock,
check again and allocate the new block if needed.

Signed-off-by: Andiry Xu 
---
 fs/nova/dax.c  | 184 +
 fs/nova/nova.h |   3 +
 2 files changed, 187 insertions(+)

diff --git a/fs/nova/dax.c b/fs/nova/dax.c
index 8624ce4..e639b23 100644
--- a/fs/nova/dax.c
+++ b/fs/nova/dax.c
@@ -731,3 +731,187 @@ ssize_t nova_inplace_file_write(struct file *filp,
 
return ret;
 }
+
+/*
+ * return > 0, # of blocks mapped or allocated.
+ * return = 0, if plain lookup failed.
+ * return < 0, error case.
+ */
+static int nova_dax_get_blocks(struct inode *inode, sector_t iblock,
+   unsigned long max_blocks, u32 *bno, bool *new, bool *boundary,
+   int create)
+{
+   struct super_block *sb = inode->i_sb;
+   struct nova_inode *pi;
+   struct nova_inode_info *si = NOVA_I(inode);
+   struct nova_inode_info_header *sih = >header;
+   struct nova_file_write_entry *entry = NULL;
+   struct nova_file_write_item entry_item;
+   struct list_head item_head;
+   struct nova_inode_update update;
+   u32 time;
+   unsigned long nvmm = 0;
+   unsigned long blocknr = 0;
+   u64 epoch_id;
+   int num_blocks = 0;
+   int inplace = 0;
+   int allocated = 0;
+   int locked = 0;
+   int check_next;
+   int ret = 0;
+   timing_t get_block_time;
+
+
+   if (max_blocks == 0)
+   return 0;
+
+   NOVA_START_TIMING(dax_get_block_t, get_block_time);
+   INIT_LIST_HEAD(_head);
+
+   nova_dbgv("%s: pgoff %lu, num %lu, create %d\n",
+   __func__, iblock, max_blocks, create);
+
+   epoch_id = nova_get_epoch_id(sb);
+
+   check_next = 0;
+   sih_lock_shared(sih);
+
+again:
+   num_blocks = nova_check_existing_entry(sb, inode, max_blocks,
+   iblock, , check_next,
+   epoch_id, );
+
+   if (entry) {
+   if (create == 0 || inplace) {
+   nvmm = get_nvmm(sb, sih, entry, iblock);
+   nova_dbgv("%s: found pgoff %lu, block %lu\n",
+   __func__, iblock, nvmm);
+   goto out;
+   }
+   }
+
+   if (create == 0) {
+   num_blocks = 0;
+   goto out1;
+   }
+
+   if (locked == 0) {
+   sih_unlock_shared(sih);
+   sih_lock(sih);
+   locked = 1;
+   /* Check again incase someone has done it for us */
+   check_next = 1;
+   goto again;
+   }
+
+   pi = nova_get_inode(sb, inode);
+   inode->i_ctime = inode->i_mtime = current_time(inode);
+   time = current_time(inode).tv_sec;
+   update.tail = sih->log_tail;
+
+   /* Return initialized blocks to the user */
+   allocated = nova_new_data_blocks(sb, sih, , iblock,
+num_blocks, ALLOC_INIT_ZERO, ANY_CPU,
+ALLOC_FROM_HEAD);
+   if (allocated <= 0) {
+   nova_dbgv("%s alloc blocks failed %d\n", __func__,
+   allocated);
+   ret = allocated;
+   goto out;
+   }
+
+   num_blocks = allocated;
+   /* FIXME: how to handle file size? */
+   nova_init_file_write_item(sb, sih, _item,
+   epoch_id, iblock, num_blocks,
+   blocknr, time, inode->i_size);
+
+   list_add_tail(_item.list, _head);
+
+   nvmm = blocknr;
+
+   ret = nova_commit_writes_to_log(sb, pi, inode,
+   _head, num_blocks, 0);
+   if (ret < 0) {
+   nova_err(sb, "commit to log failed\n");
+   goto out;
+   }
+
+   NOVA_STATS_ADD(dax_new_blocks, 1);
+
+   *new = true;
+// set_buffer_new(bh);
+out:
+   if (ret < 0) {
+   nova_cleanup_incomplete_write(sb, sih, _head, 0);
+   num_blocks = ret;
+   goto out1;
+   }
+
+   *bno = nvmm;
+// if (num_blocks > 1)
+// bh->b_size = sb->s_blocksize * num_blocks;
+
+out1:
+   if (locked)
+   sih_unlock(sih);
+   else
+   sih_unlock_shared(sih);
+
+   NOVA_END_TIMING(dax_get_block_t, get_block_time);
+   return num_blocks;
+}
+
+static int nova_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
+   unsigned int flags, struct iomap *iomap)
+{
+   struct nova_sb_info *sbi = NOVA_SB(inode->i_sb);
+   unsigned int blkbits = inode->i_blkbits;
+   unsigned long first_block = offset >> blkbits;
+   unsigned long max_blocks = (length + (1 << 

[RFC v2 73/83] Dax: Add iomap operations.

2018-03-10 Thread Andiry Xu
From: Andiry Xu 

The key of iomap is dax_get_blocks(). It first takes the read lock
and lookup the block; if the block is missing, it takes write lock,
check again and allocate the new block if needed.

Signed-off-by: Andiry Xu 
---
 fs/nova/dax.c  | 184 +
 fs/nova/nova.h |   3 +
 2 files changed, 187 insertions(+)

diff --git a/fs/nova/dax.c b/fs/nova/dax.c
index 8624ce4..e639b23 100644
--- a/fs/nova/dax.c
+++ b/fs/nova/dax.c
@@ -731,3 +731,187 @@ ssize_t nova_inplace_file_write(struct file *filp,
 
return ret;
 }
+
+/*
+ * return > 0, # of blocks mapped or allocated.
+ * return = 0, if plain lookup failed.
+ * return < 0, error case.
+ */
+static int nova_dax_get_blocks(struct inode *inode, sector_t iblock,
+   unsigned long max_blocks, u32 *bno, bool *new, bool *boundary,
+   int create)
+{
+   struct super_block *sb = inode->i_sb;
+   struct nova_inode *pi;
+   struct nova_inode_info *si = NOVA_I(inode);
+   struct nova_inode_info_header *sih = >header;
+   struct nova_file_write_entry *entry = NULL;
+   struct nova_file_write_item entry_item;
+   struct list_head item_head;
+   struct nova_inode_update update;
+   u32 time;
+   unsigned long nvmm = 0;
+   unsigned long blocknr = 0;
+   u64 epoch_id;
+   int num_blocks = 0;
+   int inplace = 0;
+   int allocated = 0;
+   int locked = 0;
+   int check_next;
+   int ret = 0;
+   timing_t get_block_time;
+
+
+   if (max_blocks == 0)
+   return 0;
+
+   NOVA_START_TIMING(dax_get_block_t, get_block_time);
+   INIT_LIST_HEAD(_head);
+
+   nova_dbgv("%s: pgoff %lu, num %lu, create %d\n",
+   __func__, iblock, max_blocks, create);
+
+   epoch_id = nova_get_epoch_id(sb);
+
+   check_next = 0;
+   sih_lock_shared(sih);
+
+again:
+   num_blocks = nova_check_existing_entry(sb, inode, max_blocks,
+   iblock, , check_next,
+   epoch_id, );
+
+   if (entry) {
+   if (create == 0 || inplace) {
+   nvmm = get_nvmm(sb, sih, entry, iblock);
+   nova_dbgv("%s: found pgoff %lu, block %lu\n",
+   __func__, iblock, nvmm);
+   goto out;
+   }
+   }
+
+   if (create == 0) {
+   num_blocks = 0;
+   goto out1;
+   }
+
+   if (locked == 0) {
+   sih_unlock_shared(sih);
+   sih_lock(sih);
+   locked = 1;
+   /* Check again incase someone has done it for us */
+   check_next = 1;
+   goto again;
+   }
+
+   pi = nova_get_inode(sb, inode);
+   inode->i_ctime = inode->i_mtime = current_time(inode);
+   time = current_time(inode).tv_sec;
+   update.tail = sih->log_tail;
+
+   /* Return initialized blocks to the user */
+   allocated = nova_new_data_blocks(sb, sih, , iblock,
+num_blocks, ALLOC_INIT_ZERO, ANY_CPU,
+ALLOC_FROM_HEAD);
+   if (allocated <= 0) {
+   nova_dbgv("%s alloc blocks failed %d\n", __func__,
+   allocated);
+   ret = allocated;
+   goto out;
+   }
+
+   num_blocks = allocated;
+   /* FIXME: how to handle file size? */
+   nova_init_file_write_item(sb, sih, _item,
+   epoch_id, iblock, num_blocks,
+   blocknr, time, inode->i_size);
+
+   list_add_tail(_item.list, _head);
+
+   nvmm = blocknr;
+
+   ret = nova_commit_writes_to_log(sb, pi, inode,
+   _head, num_blocks, 0);
+   if (ret < 0) {
+   nova_err(sb, "commit to log failed\n");
+   goto out;
+   }
+
+   NOVA_STATS_ADD(dax_new_blocks, 1);
+
+   *new = true;
+// set_buffer_new(bh);
+out:
+   if (ret < 0) {
+   nova_cleanup_incomplete_write(sb, sih, _head, 0);
+   num_blocks = ret;
+   goto out1;
+   }
+
+   *bno = nvmm;
+// if (num_blocks > 1)
+// bh->b_size = sb->s_blocksize * num_blocks;
+
+out1:
+   if (locked)
+   sih_unlock(sih);
+   else
+   sih_unlock_shared(sih);
+
+   NOVA_END_TIMING(dax_get_block_t, get_block_time);
+   return num_blocks;
+}
+
+static int nova_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
+   unsigned int flags, struct iomap *iomap)
+{
+   struct nova_sb_info *sbi = NOVA_SB(inode->i_sb);
+   unsigned int blkbits = inode->i_blkbits;
+   unsigned long first_block = offset >> blkbits;
+   unsigned long max_blocks = (length + (1 << blkbits) - 1) >> blkbits;
+   bool new =