diff -drupN a/fs/fat/file.c b/fs/fat/file.c --- a/fs/fat/file.c 2018-08-06 17:23:04.000000000 +0300 +++ b/fs/fat/file.c 2022-06-12 05:28:14.000000000 +0300 @@ -17,6 +17,10 @@ #include #include "fat.h" +#ifdef CONFIG_PRELLOCATE_FLAG +static DEFINE_MUTEX (i_mutex); +#endif + static long fat_fallocate(struct file *file, int mode, loff_t offset, loff_t len); @@ -121,6 +125,37 @@ static int fat_ioctl_get_volume_id(struc return put_user(sbi->vol_id, user_attr); } +static int fat_ioctl_fitrim(struct inode *inode, unsigned long arg) +{ + struct super_block *sb = inode->i_sb; + struct fstrim_range __user *user_range; + struct fstrim_range range; + struct request_queue *q = bdev_get_queue(sb->s_bdev); + int err; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (!blk_queue_discard(q)) + return -EOPNOTSUPP; + + user_range = (struct fstrim_range __user *)arg; + if (copy_from_user(&range, user_range, sizeof(range))) + return -EFAULT; + + range.minlen = max_t(unsigned int, range.minlen, + q->limits.discard_granularity); + + err = fat_trim_fs(inode, &range); + if (err < 0) + return err; + + if (copy_to_user(user_range, &range, sizeof(range))) + return -EFAULT; + + return 0; +} + long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = file_inode(filp); @@ -133,6 +168,8 @@ long fat_generic_ioctl(struct file *filp return fat_ioctl_set_attributes(filp, user_attr); case FAT_IOCTL_GET_VOLUME_ID: return fat_ioctl_get_volume_id(inode, user_attr); + case FITRIM: + return fat_ioctl_fitrim(inode, arg); default: return -ENOTTY; /* Inappropriate ioctl for device */ } @@ -168,6 +205,17 @@ int fat_file_fsync(struct file *filp, lo return res ? res : err; } +#ifdef CONFIG_OPTIMIZE_METADATA_REFRESH +int fat_file_flush(struct file *file, fl_owner_t id) +{ + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + + inode->i_sb->s_op->write_inode(inode, NULL); + + return 0; +} +#endif const struct file_operations fat_file_operations = { .llseek = generic_file_llseek, @@ -182,6 +230,9 @@ const struct file_operations fat_file_op .fsync = fat_file_fsync, .splice_read = generic_file_splice_read, .fallocate = fat_fallocate, +#ifdef CONFIG_OPTIMIZE_METADATA_REFRESH + .flush = fat_file_flush, +#endif }; static int fat_cont_expand(struct inode *inode, loff_t size) @@ -246,7 +297,17 @@ static long fat_fallocate(struct file *f if (!S_ISREG(inode->i_mode)) return -EOPNOTSUPP; +#ifdef CONFIG_PRELLOCATE_FLAG + if (check_prealloc(inode)) { + fat_msg(sb, KERN_ERR, "Repeatedly called fat_fallocate"); + return -EOPNOTSUPP; + } +#endif + inode_lock(inode); +#ifdef CONFIG_PRELLOCATE_FLAG + mutex_lock(&i_mutex); +#endif if (mode & FALLOC_FL_KEEP_SIZE) { ondisksize = inode->i_blocks << 9; if ((offset + len) <= ondisksize) @@ -271,7 +332,13 @@ static long fat_fallocate(struct file *f err = fat_cont_expand(inode, (offset + len)); } +#ifdef CONFIG_PRELLOCATE_FLAG + mark_prealloc(inode); +#endif error: +#ifdef CONFIG_PRELLOCATE_FLAG + mutex_unlock(&i_mutex); +#endif inode_unlock(inode); return err; } @@ -290,14 +357,19 @@ static int fat_free(struct inode *inode, wait = IS_DIRSYNC(inode); i_start = free_start = MSDOS_I(inode)->i_start; i_logstart = MSDOS_I(inode)->i_logstart; - /* First, we write the new file size. */ +#ifdef CONFIG_TRUNCATE_NOMEM_RECLAIM + if (!skip && (inode->i_nlink == 0 || !check_prealloc(inode))) { +#else if (!skip) { +#endif MSDOS_I(inode)->i_start = 0; MSDOS_I(inode)->i_logstart = 0; } + MSDOS_I(inode)->i_attrs |= ATTR_ARCH; inode->i_ctime = inode->i_mtime = current_time(inode); + if (wait) { err = fat_sync_inode(inode); if (err) { @@ -309,7 +381,11 @@ static int fat_free(struct inode *inode, mark_inode_dirty(inode); /* Write a new EOF, and get the remaining cluster chain for freeing. */ +#ifdef CONFIG_TRUNCATE_NOMEM_RECLAIM + if (skip && !check_prealloc(inode)) { +#else if (skip) { +#endif struct fat_entry fatent; int ret, fclus, dclus; @@ -340,8 +416,17 @@ static int fat_free(struct inode *inode, free_start = ret; } - inode->i_blocks = skip << (MSDOS_SB(sb)->cluster_bits - 9); +#ifdef CONFIG_TRUNCATE_NOMEM_RECLAIM + if (check_prealloc(inode) && inode->i_nlink) { +#ifdef CONFIG_TRUNCATE_NOMEM_RECLAIM_DISCARD + fat_discard_clusters(inode, free_start); +#endif + return 0; + } +#endif + + inode->i_blocks = skip << (MSDOS_SB(sb)->cluster_bits - 9); /* Freeing the remained cluster chain */ return fat_free_clusters(inode, free_start); } @@ -468,10 +553,22 @@ int fat_setattr(struct dentry *dentry, s inode_dio_wait(inode); if (attr->ia_size > inode->i_size) { +#ifdef CONFIG_TRUNCATE_NOMEM_RECLAIM + if (!check_prealloc(inode)) { + error = fat_cont_expand(inode, attr->ia_size); + if (error || attr->ia_valid == ATTR_SIZE) + goto out; + attr->ia_valid &= ~ATTR_SIZE; + } else { + pr_err("FAT ERR: (%s) incorrectly call ftruncate", __func__); + return -EOPNOTSUPP; + } +#else error = fat_cont_expand(inode, attr->ia_size); if (error || attr->ia_valid == ATTR_SIZE) goto out; attr->ia_valid &= ~ATTR_SIZE; +#endif } }