]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - fs/f2fs/file.c
Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
[linux.git] / fs / f2fs / file.c
index eb653f700ade6b2ca10a1cb7ff4d0d7a0bb5343d..86ddbb55d2b187122453d151b35d964bf0d6b91b 100644 (file)
@@ -51,7 +51,8 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
        struct inode *inode = file_inode(vmf->vma->vm_file);
        struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
        struct dnode_of_data dn;
-       int err;
+       bool need_alloc = true;
+       int err = 0;
 
        if (unlikely(f2fs_cp_error(sbi))) {
                err = -EIO;
@@ -63,8 +64,25 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
                goto err;
        }
 
+#ifdef CONFIG_F2FS_FS_COMPRESSION
+       if (f2fs_compressed_file(inode)) {
+               int ret = f2fs_is_compressed_cluster(inode, page->index);
+
+               if (ret < 0) {
+                       err = ret;
+                       goto err;
+               } else if (ret) {
+                       if (ret < F2FS_I(inode)->i_cluster_size) {
+                               err = -EAGAIN;
+                               goto err;
+                       }
+                       need_alloc = false;
+               }
+       }
+#endif
        /* should do out of any locked page */
-       f2fs_balance_fs(sbi, true);
+       if (need_alloc)
+               f2fs_balance_fs(sbi, true);
 
        sb_start_pagefault(inode->i_sb);
 
@@ -81,15 +99,17 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
                goto out_sem;
        }
 
-       /* block allocation */
-       __do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, true);
-       set_new_dnode(&dn, inode, NULL, NULL, 0);
-       err = f2fs_get_block(&dn, page->index);
-       f2fs_put_dnode(&dn);
-       __do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, false);
-       if (err) {
-               unlock_page(page);
-               goto out_sem;
+       if (need_alloc) {
+               /* block allocation */
+               __do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, true);
+               set_new_dnode(&dn, inode, NULL, NULL, 0);
+               err = f2fs_get_block(&dn, page->index);
+               f2fs_put_dnode(&dn);
+               __do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, false);
+               if (err) {
+                       unlock_page(page);
+                       goto out_sem;
+               }
        }
 
        /* fill the page */
@@ -156,6 +176,8 @@ static inline enum cp_reason_type need_do_checkpoint(struct inode *inode)
 
        if (!S_ISREG(inode->i_mode))
                cp_reason = CP_NON_REGULAR;
+       else if (f2fs_compressed_file(inode))
+               cp_reason = CP_COMPRESSED;
        else if (inode->i_nlink != 1)
                cp_reason = CP_HARDLINK;
        else if (is_sbi_flag_set(sbi, SBI_NEED_CP))
@@ -486,6 +508,9 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)
        if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
                return -EIO;
 
+       if (!f2fs_is_compress_backend_ready(inode))
+               return -EOPNOTSUPP;
+
        /* we don't need to use inline_data strictly */
        err = f2fs_convert_inline_inode(inode);
        if (err)
@@ -493,6 +518,7 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)
 
        file_accessed(file);
        vma->vm_ops = &f2fs_file_vm_ops;
+       set_inode_flag(inode, FI_MMAP_FILE);
        return 0;
 }
 
@@ -503,6 +529,9 @@ static int f2fs_file_open(struct inode *inode, struct file *filp)
        if (err)
                return err;
 
+       if (!f2fs_is_compress_backend_ready(inode))
+               return -EOPNOTSUPP;
+
        err = fsverity_file_open(inode, filp);
        if (err)
                return err;
@@ -519,6 +548,9 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count)
        int nr_free = 0, ofs = dn->ofs_in_node, len = count;
        __le32 *addr;
        int base = 0;
+       bool compressed_cluster = false;
+       int cluster_index = 0, valid_blocks = 0;
+       int cluster_size = F2FS_I(dn->inode)->i_cluster_size;
 
        if (IS_INODE(dn->node_page) && f2fs_has_extra_attr(dn->inode))
                base = get_extra_isize(dn->inode);
@@ -526,26 +558,43 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count)
        raw_node = F2FS_NODE(dn->node_page);
        addr = blkaddr_in_node(raw_node) + base + ofs;
 
-       for (; count > 0; count--, addr++, dn->ofs_in_node++) {
+       /* Assumption: truncateion starts with cluster */
+       for (; count > 0; count--, addr++, dn->ofs_in_node++, cluster_index++) {
                block_t blkaddr = le32_to_cpu(*addr);
 
+               if (f2fs_compressed_file(dn->inode) &&
+                                       !(cluster_index & (cluster_size - 1))) {
+                       if (compressed_cluster)
+                               f2fs_i_compr_blocks_update(dn->inode,
+                                                       valid_blocks, false);
+                       compressed_cluster = (blkaddr == COMPRESS_ADDR);
+                       valid_blocks = 0;
+               }
+
                if (blkaddr == NULL_ADDR)
                        continue;
 
                dn->data_blkaddr = NULL_ADDR;
                f2fs_set_data_blkaddr(dn);
 
-               if (__is_valid_data_blkaddr(blkaddr) &&
-                       !f2fs_is_valid_blkaddr(sbi, blkaddr,
+               if (__is_valid_data_blkaddr(blkaddr)) {
+                       if (!f2fs_is_valid_blkaddr(sbi, blkaddr,
                                        DATA_GENERIC_ENHANCE))
-                       continue;
+                               continue;
+                       if (compressed_cluster)
+                               valid_blocks++;
+               }
 
-               f2fs_invalidate_blocks(sbi, blkaddr);
                if (dn->ofs_in_node == 0 && IS_INODE(dn->node_page))
                        clear_inode_flag(dn->inode, FI_FIRST_BLOCK_WRITTEN);
+
+               f2fs_invalidate_blocks(sbi, blkaddr);
                nr_free++;
        }
 
+       if (compressed_cluster)
+               f2fs_i_compr_blocks_update(dn->inode, valid_blocks, false);
+
        if (nr_free) {
                pgoff_t fofs;
                /*
@@ -588,6 +637,9 @@ static int truncate_partial_data_page(struct inode *inode, u64 from,
                return 0;
        }
 
+       if (f2fs_compressed_file(inode))
+               return 0;
+
        page = f2fs_get_lock_data_page(inode, index, true);
        if (IS_ERR(page))
                return PTR_ERR(page) == -ENOENT ? 0 : PTR_ERR(page);
@@ -603,7 +655,7 @@ static int truncate_partial_data_page(struct inode *inode, u64 from,
        return 0;
 }
 
-int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock)
+static int do_truncate_blocks(struct inode *inode, u64 from, bool lock)
 {
        struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
        struct dnode_of_data dn;
@@ -668,6 +720,28 @@ int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock)
        return err;
 }
 
+int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock)
+{
+       u64 free_from = from;
+
+       /*
+        * for compressed file, only support cluster size
+        * aligned truncation.
+        */
+       if (f2fs_compressed_file(inode)) {
+               size_t cluster_shift = PAGE_SHIFT +
+                                       F2FS_I(inode)->i_log_cluster_size;
+               size_t cluster_mask = (1 << cluster_shift) - 1;
+
+               free_from = from >> cluster_shift;
+               if (from & cluster_mask)
+                       free_from++;
+               free_from <<= cluster_shift;
+       }
+
+       return do_truncate_blocks(inode, free_from, lock);
+}
+
 int f2fs_truncate(struct inode *inode)
 {
        int err;
@@ -787,6 +861,10 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
        if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
                return -EIO;
 
+       if ((attr->ia_valid & ATTR_SIZE) &&
+               !f2fs_is_compress_backend_ready(inode))
+               return -EOPNOTSUPP;
+
        err = setattr_prepare(dentry, attr);
        if (err)
                return err;
@@ -1027,8 +1105,8 @@ static int __read_out_blkaddrs(struct inode *inode, block_t *blkaddr,
        } else if (ret == -ENOENT) {
                if (dn.max_level == 0)
                        return -ENOENT;
-               done = min((pgoff_t)ADDRS_PER_BLOCK(inode) - dn.ofs_in_node,
-                                                                       len);
+               done = min((pgoff_t)ADDRS_PER_BLOCK(inode) -
+                                               dn.ofs_in_node, len);
                blkaddr += done;
                do_replace += done;
                goto next;
@@ -1564,7 +1642,7 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
 next_alloc:
                if (has_not_enough_free_secs(sbi, 0,
                        GET_SEC_FROM_SEG(sbi, overprovision_segments(sbi)))) {
-                       mutex_lock(&sbi->gc_mutex);
+                       down_write(&sbi->gc_lock);
                        err = f2fs_gc(sbi, true, false, NULL_SEGNO);
                        if (err && err != -ENODATA && err != -EAGAIN)
                                goto out_err;
@@ -1622,6 +1700,8 @@ static long f2fs_fallocate(struct file *file, int mode,
                return -EIO;
        if (!f2fs_is_checkpoint_ready(F2FS_I_SB(inode)))
                return -ENOSPC;
+       if (!f2fs_is_compress_backend_ready(inode))
+               return -EOPNOTSUPP;
 
        /* f2fs only support ->fallocate for regular file */
        if (!S_ISREG(inode->i_mode))
@@ -1631,6 +1711,11 @@ static long f2fs_fallocate(struct file *file, int mode,
                (mode & (FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE)))
                return -EOPNOTSUPP;
 
+       if (f2fs_compressed_file(inode) &&
+               (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE |
+                       FALLOC_FL_ZERO_RANGE | FALLOC_FL_INSERT_RANGE)))
+               return -EOPNOTSUPP;
+
        if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
                        FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE |
                        FALLOC_FL_INSERT_RANGE))
@@ -1720,7 +1805,40 @@ static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask)
                        return -ENOTEMPTY;
        }
 
+       if (iflags & (F2FS_COMPR_FL | F2FS_NOCOMP_FL)) {
+               if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
+                       return -EOPNOTSUPP;
+               if ((iflags & F2FS_COMPR_FL) && (iflags & F2FS_NOCOMP_FL))
+                       return -EINVAL;
+       }
+
+       if ((iflags ^ fi->i_flags) & F2FS_COMPR_FL) {
+               if (S_ISREG(inode->i_mode) &&
+                       (fi->i_flags & F2FS_COMPR_FL || i_size_read(inode) ||
+                                               F2FS_HAS_BLOCKS(inode)))
+                       return -EINVAL;
+               if (iflags & F2FS_NOCOMP_FL)
+                       return -EINVAL;
+               if (iflags & F2FS_COMPR_FL) {
+                       int err = f2fs_convert_inline_inode(inode);
+
+                       if (err)
+                               return err;
+
+                       if (!f2fs_may_compress(inode))
+                               return -EINVAL;
+
+                       set_compress_context(inode);
+               }
+       }
+       if ((iflags ^ fi->i_flags) & F2FS_NOCOMP_FL) {
+               if (fi->i_flags & F2FS_COMPR_FL)
+                       return -EINVAL;
+       }
+
        fi->i_flags = iflags | (fi->i_flags & ~mask);
+       f2fs_bug_on(F2FS_I_SB(inode), (fi->i_flags & F2FS_COMPR_FL) &&
+                                       (fi->i_flags & F2FS_NOCOMP_FL));
 
        if (fi->i_flags & F2FS_PROJINHERIT_FL)
                set_inode_flag(inode, FI_PROJ_INHERIT);
@@ -1746,11 +1864,13 @@ static const struct {
        u32 iflag;
        u32 fsflag;
 } f2fs_fsflags_map[] = {
+       { F2FS_COMPR_FL,        FS_COMPR_FL },
        { F2FS_SYNC_FL,         FS_SYNC_FL },
        { F2FS_IMMUTABLE_FL,    FS_IMMUTABLE_FL },
        { F2FS_APPEND_FL,       FS_APPEND_FL },
        { F2FS_NODUMP_FL,       FS_NODUMP_FL },
        { F2FS_NOATIME_FL,      FS_NOATIME_FL },
+       { F2FS_NOCOMP_FL,       FS_NOCOMP_FL },
        { F2FS_INDEX_FL,        FS_INDEX_FL },
        { F2FS_DIRSYNC_FL,      FS_DIRSYNC_FL },
        { F2FS_PROJINHERIT_FL,  FS_PROJINHERIT_FL },
@@ -1758,11 +1878,13 @@ static const struct {
 };
 
 #define F2FS_GETTABLE_FS_FL (          \
+               FS_COMPR_FL |           \
                FS_SYNC_FL |            \
                FS_IMMUTABLE_FL |       \
                FS_APPEND_FL |          \
                FS_NODUMP_FL |          \
                FS_NOATIME_FL |         \
+               FS_NOCOMP_FL |          \
                FS_INDEX_FL |           \
                FS_DIRSYNC_FL |         \
                FS_PROJINHERIT_FL |     \
@@ -1773,11 +1895,13 @@ static const struct {
                FS_CASEFOLD_FL)
 
 #define F2FS_SETTABLE_FS_FL (          \
+               FS_COMPR_FL |           \
                FS_SYNC_FL |            \
                FS_IMMUTABLE_FL |       \
                FS_APPEND_FL |          \
                FS_NODUMP_FL |          \
                FS_NOATIME_FL |         \
+               FS_NOCOMP_FL |          \
                FS_DIRSYNC_FL |         \
                FS_PROJINHERIT_FL |     \
                FS_CASEFOLD_FL)
@@ -1898,6 +2022,8 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
 
        inode_lock(inode);
 
+       f2fs_disable_compressed_file(inode);
+
        if (f2fs_is_atomic_file(inode)) {
                if (is_inode_flag_set(inode, FI_ATOMIC_REVOKE_REQUEST))
                        ret = -EINVAL;
@@ -1936,7 +2062,6 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
 
        f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
        F2FS_I(inode)->inmem_task = current;
-       stat_inc_atomic_write(inode);
        stat_update_max_atomic_write(inode);
 out:
        inode_unlock(inode);
@@ -2325,12 +2450,12 @@ static int f2fs_ioc_gc(struct file *filp, unsigned long arg)
                return ret;
 
        if (!sync) {
-               if (!mutex_trylock(&sbi->gc_mutex)) {
+               if (!down_write_trylock(&sbi->gc_lock)) {
                        ret = -EBUSY;
                        goto out;
                }
        } else {
-               mutex_lock(&sbi->gc_mutex);
+               down_write(&sbi->gc_lock);
        }
 
        ret = f2fs_gc(sbi, sync, true, NULL_SEGNO);
@@ -2368,12 +2493,12 @@ static int f2fs_ioc_gc_range(struct file *filp, unsigned long arg)
 
 do_more:
        if (!range.sync) {
-               if (!mutex_trylock(&sbi->gc_mutex)) {
+               if (!down_write_trylock(&sbi->gc_lock)) {
                        ret = -EBUSY;
                        goto out;
                }
        } else {
-               mutex_lock(&sbi->gc_mutex);
+               down_write(&sbi->gc_lock);
        }
 
        ret = f2fs_gc(sbi, range.sync, true, GET_SEGNO(sbi, range.start));
@@ -2804,7 +2929,7 @@ static int f2fs_ioc_flush_device(struct file *filp, unsigned long arg)
        end_segno = min(start_segno + range.segments, dev_end_segno);
 
        while (start_segno < end_segno) {
-               if (!mutex_trylock(&sbi->gc_mutex)) {
+               if (!down_write_trylock(&sbi->gc_lock)) {
                        ret = -EBUSY;
                        goto out;
                }
@@ -3099,10 +3224,16 @@ static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg)
                ret = -EAGAIN;
                goto out;
        }
+
        ret = f2fs_convert_inline_inode(inode);
        if (ret)
                goto out;
 
+       if (f2fs_disable_compressed_file(inode)) {
+               ret = -EOPNOTSUPP;
+               goto out;
+       }
+
        set_inode_flag(inode, FI_PIN_FILE);
        ret = F2FS_I(inode)->i_gc_failures[GC_FAILURE_PIN];
 done:
@@ -3351,6 +3482,17 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
        }
 }
 
+static ssize_t f2fs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
+{
+       struct file *file = iocb->ki_filp;
+       struct inode *inode = file_inode(file);
+
+       if (!f2fs_is_compress_backend_ready(inode))
+               return -EOPNOTSUPP;
+
+       return generic_file_read_iter(iocb, iter);
+}
+
 static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *file = iocb->ki_filp;
@@ -3362,6 +3504,9 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
                goto out;
        }
 
+       if (!f2fs_is_compress_backend_ready(inode))
+               return -EOPNOTSUPP;
+
        if (iocb->ki_flags & IOCB_NOWAIT) {
                if (!inode_trylock(inode)) {
                        ret = -EAGAIN;
@@ -3499,7 +3644,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
 const struct file_operations f2fs_file_operations = {
        .llseek         = f2fs_llseek,
-       .read_iter      = generic_file_read_iter,
+       .read_iter      = f2fs_file_read_iter,
        .write_iter     = f2fs_file_write_iter,
        .open           = f2fs_file_open,
        .release        = f2fs_release_file,