Merge branch 'xarray' of git://git.infradead.org/users/willy/linux-dax
[platform/kernel/linux-starfive.git] / fs / ext4 / inode.c
index 57bad3e..05f01fb 100644 (file)
@@ -577,8 +577,8 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
                                EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
                if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) &&
                    !(status & EXTENT_STATUS_WRITTEN) &&
-                   ext4_find_delalloc_range(inode, map->m_lblk,
-                                            map->m_lblk + map->m_len - 1))
+                   ext4_es_scan_range(inode, &ext4_es_is_delayed, map->m_lblk,
+                                      map->m_lblk + map->m_len - 1))
                        status |= EXTENT_STATUS_DELAYED;
                ret = ext4_es_insert_extent(inode, map->m_lblk,
                                            map->m_len, map->m_pblk, status);
@@ -701,8 +701,8 @@ found:
                                EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
                if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) &&
                    !(status & EXTENT_STATUS_WRITTEN) &&
-                   ext4_find_delalloc_range(inode, map->m_lblk,
-                                            map->m_lblk + map->m_len - 1))
+                   ext4_es_scan_range(inode, &ext4_es_is_delayed, map->m_lblk,
+                                      map->m_lblk + map->m_len - 1))
                        status |= EXTENT_STATUS_DELAYED;
                ret = ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
                                            map->m_pblk, status);
@@ -1595,7 +1595,7 @@ static int ext4_da_reserve_space(struct inode *inode)
        return 0;       /* success */
 }
 
-static void ext4_da_release_space(struct inode *inode, int to_free)
+void ext4_da_release_space(struct inode *inode, int to_free)
 {
        struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
        struct ext4_inode_info *ei = EXT4_I(inode);
@@ -1634,13 +1634,11 @@ static void ext4_da_page_release_reservation(struct page *page,
                                             unsigned int offset,
                                             unsigned int length)
 {
-       int to_release = 0, contiguous_blks = 0;
+       int contiguous_blks = 0;
        struct buffer_head *head, *bh;
        unsigned int curr_off = 0;
        struct inode *inode = page->mapping->host;
-       struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
        unsigned int stop = offset + length;
-       int num_clusters;
        ext4_fsblk_t lblk;
 
        BUG_ON(stop > PAGE_SIZE || stop < length);
@@ -1654,7 +1652,6 @@ static void ext4_da_page_release_reservation(struct page *page,
                        break;
 
                if ((offset <= curr_off) && (buffer_delay(bh))) {
-                       to_release++;
                        contiguous_blks++;
                        clear_buffer_delay(bh);
                } else if (contiguous_blks) {
@@ -1662,7 +1659,7 @@ static void ext4_da_page_release_reservation(struct page *page,
                               (PAGE_SHIFT - inode->i_blkbits);
                        lblk += (curr_off >> inode->i_blkbits) -
                                contiguous_blks;
-                       ext4_es_remove_extent(inode, lblk, contiguous_blks);
+                       ext4_es_remove_blks(inode, lblk, contiguous_blks);
                        contiguous_blks = 0;
                }
                curr_off = next_off;
@@ -1671,21 +1668,9 @@ static void ext4_da_page_release_reservation(struct page *page,
        if (contiguous_blks) {
                lblk = page->index << (PAGE_SHIFT - inode->i_blkbits);
                lblk += (curr_off >> inode->i_blkbits) - contiguous_blks;
-               ext4_es_remove_extent(inode, lblk, contiguous_blks);
+               ext4_es_remove_blks(inode, lblk, contiguous_blks);
        }
 
-       /* If we have released all the blocks belonging to a cluster, then we
-        * need to release the reserved space for that cluster. */
-       num_clusters = EXT4_NUM_B2C(sbi, to_release);
-       while (num_clusters > 0) {
-               lblk = (page->index << (PAGE_SHIFT - inode->i_blkbits)) +
-                       ((num_clusters - 1) << sbi->s_cluster_bits);
-               if (sbi->s_cluster_ratio == 1 ||
-                   !ext4_find_delalloc_cluster(inode, lblk))
-                       ext4_da_release_space(inode, 1);
-
-               num_clusters--;
-       }
 }
 
 /*
@@ -1781,6 +1766,65 @@ static int ext4_bh_delay_or_unwritten(handle_t *handle, struct buffer_head *bh)
 }
 
 /*
+ * ext4_insert_delayed_block - adds a delayed block to the extents status
+ *                             tree, incrementing the reserved cluster/block
+ *                             count or making a pending reservation
+ *                             where needed
+ *
+ * @inode - file containing the newly added block
+ * @lblk - logical block to be added
+ *
+ * Returns 0 on success, negative error code on failure.
+ */
+static int ext4_insert_delayed_block(struct inode *inode, ext4_lblk_t lblk)
+{
+       struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
+       int ret;
+       bool allocated = false;
+
+       /*
+        * If the cluster containing lblk is shared with a delayed,
+        * written, or unwritten extent in a bigalloc file system, it's
+        * already been accounted for and does not need to be reserved.
+        * A pending reservation must be made for the cluster if it's
+        * shared with a written or unwritten extent and doesn't already
+        * have one.  Written and unwritten extents can be purged from the
+        * extents status tree if the system is under memory pressure, so
+        * it's necessary to examine the extent tree if a search of the
+        * extents status tree doesn't get a match.
+        */
+       if (sbi->s_cluster_ratio == 1) {
+               ret = ext4_da_reserve_space(inode);
+               if (ret != 0)   /* ENOSPC */
+                       goto errout;
+       } else {   /* bigalloc */
+               if (!ext4_es_scan_clu(inode, &ext4_es_is_delonly, lblk)) {
+                       if (!ext4_es_scan_clu(inode,
+                                             &ext4_es_is_mapped, lblk)) {
+                               ret = ext4_clu_mapped(inode,
+                                                     EXT4_B2C(sbi, lblk));
+                               if (ret < 0)
+                                       goto errout;
+                               if (ret == 0) {
+                                       ret = ext4_da_reserve_space(inode);
+                                       if (ret != 0)   /* ENOSPC */
+                                               goto errout;
+                               } else {
+                                       allocated = true;
+                               }
+                       } else {
+                               allocated = true;
+                       }
+               }
+       }
+
+       ret = ext4_es_insert_delayed_block(inode, lblk, allocated);
+
+errout:
+       return ret;
+}
+
+/*
  * This function is grabs code from the very beginning of
  * ext4_map_blocks, but assumes that the caller is from delayed write
  * time. This function looks up the requested blocks and sets the
@@ -1859,28 +1903,14 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
 add_delayed:
        if (retval == 0) {
                int ret;
+
                /*
                 * XXX: __block_prepare_write() unmaps passed block,
                 * is it OK?
                 */
-               /*
-                * If the block was allocated from previously allocated cluster,
-                * then we don't need to reserve it again. However we still need
-                * to reserve metadata for every block we're going to write.
-                */
-               if (EXT4_SB(inode->i_sb)->s_cluster_ratio == 1 ||
-                   !ext4_find_delalloc_cluster(inode, map->m_lblk)) {
-                       ret = ext4_da_reserve_space(inode);
-                       if (ret) {
-                               /* not enough space to reserve */
-                               retval = ret;
-                               goto out_unlock;
-                       }
-               }
 
-               ret = ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
-                                           ~0, EXTENT_STATUS_DELAYED);
-               if (ret) {
+               ret = ext4_insert_delayed_block(inode, map->m_lblk);
+               if (ret != 0) {
                        retval = ret;
                        goto out_unlock;
                }
@@ -3450,7 +3480,8 @@ static int ext4_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
                        ext4_lblk_t end = map.m_lblk + map.m_len - 1;
                        struct extent_status es;
 
-                       ext4_es_find_delayed_extent_range(inode, map.m_lblk, end, &es);
+                       ext4_es_find_extent_range(inode, &ext4_es_is_delayed,
+                                                 map.m_lblk, end, &es);
 
                        if (!es.es_len || es.es_lblk > end) {
                                /* entire range is a hole */
@@ -6153,13 +6184,14 @@ static int ext4_bh_unmapped(handle_t *handle, struct buffer_head *bh)
        return !buffer_mapped(bh);
 }
 
-int ext4_page_mkwrite(struct vm_fault *vmf)
+vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf)
 {
        struct vm_area_struct *vma = vmf->vma;
        struct page *page = vmf->page;
        loff_t size;
        unsigned long len;
-       int ret;
+       int err;
+       vm_fault_t ret;
        struct file *file = vma->vm_file;
        struct inode *inode = file_inode(file);
        struct address_space *mapping = inode->i_mapping;
@@ -6172,8 +6204,8 @@ int ext4_page_mkwrite(struct vm_fault *vmf)
 
        down_read(&EXT4_I(inode)->i_mmap_sem);
 
-       ret = ext4_convert_inline_data(inode);
-       if (ret)
+       err = ext4_convert_inline_data(inode);
+       if (err)
                goto out_ret;
 
        /* Delalloc case is easy... */
@@ -6181,9 +6213,9 @@ int ext4_page_mkwrite(struct vm_fault *vmf)
            !ext4_should_journal_data(inode) &&
            !ext4_nonda_switch(inode->i_sb)) {
                do {
-                       ret = block_page_mkwrite(vma, vmf,
+                       err = block_page_mkwrite(vma, vmf,
                                                   ext4_da_get_block_prep);
-               } while (ret == -ENOSPC &&
+               } while (err == -ENOSPC &&
                       ext4_should_retry_alloc(inode->i_sb, &retries));
                goto out_ret;
        }
@@ -6228,8 +6260,8 @@ retry_alloc:
                ret = VM_FAULT_SIGBUS;
                goto out;
        }
-       ret = block_page_mkwrite(vma, vmf, get_block);
-       if (!ret && ext4_should_journal_data(inode)) {
+       err = block_page_mkwrite(vma, vmf, get_block);
+       if (!err && ext4_should_journal_data(inode)) {
                if (ext4_walk_page_buffers(handle, page_buffers(page), 0,
                          PAGE_SIZE, NULL, do_journal_get_write_access)) {
                        unlock_page(page);
@@ -6240,24 +6272,24 @@ retry_alloc:
                ext4_set_inode_state(inode, EXT4_STATE_JDATA);
        }
        ext4_journal_stop(handle);
-       if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
+       if (err == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
                goto retry_alloc;
 out_ret:
-       ret = block_page_mkwrite_return(ret);
+       ret = block_page_mkwrite_return(err);
 out:
        up_read(&EXT4_I(inode)->i_mmap_sem);
        sb_end_pagefault(inode->i_sb);
        return ret;
 }
 
-int ext4_filemap_fault(struct vm_fault *vmf)
+vm_fault_t ext4_filemap_fault(struct vm_fault *vmf)
 {
        struct inode *inode = file_inode(vmf->vma->vm_file);
-       int err;
+       vm_fault_t ret;
 
        down_read(&EXT4_I(inode)->i_mmap_sem);
-       err = filemap_fault(vmf);
+       ret = filemap_fault(vmf);
        up_read(&EXT4_I(inode)->i_mmap_sem);
 
-       return err;
+       return ret;
 }