diff -drupN a/fs/fat/fatent.c b/fs/fat/fatent.c --- a/fs/fat/fatent.c 2018-08-06 17:23:04.000000000 +0300 +++ b/fs/fat/fatent.c 2022-06-12 05:28:14.000000000 +0300 @@ -380,6 +380,9 @@ static int fat_mirror_bhs(struct super_b int err, n, copy; err = 0; +#ifdef CONFIG_FAT1_UPDATE_ONLY + return 0; +#endif for (copy = 1; copy < sbi->fats; copy++) { sector_t backup_fat = sbi->fat_length * copy; @@ -458,6 +461,51 @@ static void fat_collect_bhs(struct buffe } } } +#ifdef CONFIG_PRELLOCATE_FLAG +int fat_caculate_cluster(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct msdos_sb_info *sbi = MSDOS_SB(sb); + const struct fatent_operations *ops = sbi->fatent_ops; + struct fat_entry fatent; + unsigned int nr_cluster = 0; + int err = 0, cluster = 0; + fatent_init(&fatent); + fatent_set_entry(&fatent, MSDOS_I(inode)->i_start); + err = fat_ent_read_block(sb, &fatent); + if (err) + goto error; + + while (ops->ent_get(&fatent) != FAT_ENT_EOF) { + cluster = ops->ent_get(&fatent); + if (cluster < 0) { + err = cluster; + pr_err("FAT ERR: %s: invalid FAT entry value", + __func__); + goto error; + } else if (cluster == FAT_ENT_FREE) { + pr_err("FAT ERR: %s: deleting FAT entry beyond EOF", + __func__); + err = -EIO; + goto error; + } + nr_cluster++; + fatent_set_entry(&fatent, cluster); + err = fat_ent_read_block(sb, &fatent); + if (err) + goto error; + } +error: + fatent_brelse(&fatent); + if (err) + return err; + + if (nr_cluster) + return nr_cluster + 1; + else + return 0; +} +#endif int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster) { @@ -468,6 +516,8 @@ int fat_alloc_clusters(struct inode *ino struct buffer_head *bhs[MAX_BUF_PER_PAGE]; int i, count, err, nr_bhs, idx_clus; + if (nr_cluster > (MAX_BUF_PER_PAGE / 2)) + printk("nr_cluster %d,max nr_cluster %lu\n", nr_cluster, (MAX_BUF_PER_PAGE / 2)); BUG_ON(nr_cluster > (MAX_BUF_PER_PAGE / 2)); /* fixed limit */ lock_fat(sbi); @@ -547,6 +597,57 @@ out: return err; } +#ifdef CONFIG_TRUNCATE_NOMEM_RECLAIM_DISCARD +int fat_discard_clusters(struct inode *inode, int cluster) +{ + struct super_block *sb = inode->i_sb; + struct msdos_sb_info *sbi = MSDOS_SB(sb); + struct fat_entry fatent; + int err; + int first_cl = cluster; + + fatent_init(&fatent); + lock_fat(sbi); + do { + cluster = fat_ent_read(inode, &fatent, cluster); + if (cluster < 0) { + err = cluster; + goto error; + } else if (cluster == FAT_ENT_FREE) { + fat_fs_error(sb, "%s: deleting FAT entry beyond EOF", + __func__); + err = -EIO; + goto error; + } + + if (sbi->options.discard) { + /* + * Issue discard for the sectors we no longer + * care about, batching contiguous clusters + * into one request + */ + if (cluster != fatent.entry + 1) { + int nr_clus = fatent.entry - first_cl + 1; + + sb_issue_discard(sb, + fat_clus_to_blknr(sbi, first_cl), + nr_clus * sbi->sec_per_clus, + GFP_NOFS, 0); + + first_cl = cluster; + } + } + + } while (cluster != FAT_ENT_EOF); +error: + fatent_brelse(&fatent); + unlock_fat(sbi); + + return err; +} +EXPORT_SYMBOL_GPL(fat_discard_clusters); +#endif + int fat_free_clusters(struct inode *inode, int cluster) { struct super_block *sb = inode->i_sb; @@ -690,3 +791,104 @@ out: unlock_fat(sbi); return err; } + +static int fat_trim_clusters(struct super_block *sb, u32 clus, u32 nr_clus) +{ + struct msdos_sb_info *sbi = MSDOS_SB(sb); + return sb_issue_discard(sb, fat_clus_to_blknr(sbi, clus), + nr_clus * sbi->sec_per_clus, GFP_NOFS, 0); +} + +int fat_trim_fs(struct inode *inode, struct fstrim_range *range) +{ + struct super_block *sb = inode->i_sb; + struct msdos_sb_info *sbi = MSDOS_SB(sb); + const struct fatent_operations *ops = sbi->fatent_ops; + struct fat_entry fatent; + u64 ent_start, ent_end, minlen, trimmed = 0; + u32 free = 0; + unsigned long reada_blocks, reada_mask, cur_block = 0; + int err = 0; + + /* + * FAT data is organized as clusters, trim at the granulary of cluster. + * + * fstrim_range is in byte, convert vaules to cluster index. + * Treat sectors before data region as all used, not to trim them. + */ + ent_start = max_t(u64, range->start>>sbi->cluster_bits, FAT_START_ENT); + ent_end = ent_start + (range->len >> sbi->cluster_bits) - 1; + minlen = range->minlen >> sbi->cluster_bits; + + if (ent_start >= sbi->max_cluster || range->len < sbi->cluster_size) + return -EINVAL; + if (ent_end >= sbi->max_cluster) + ent_end = sbi->max_cluster - 1; + + reada_blocks = FAT_READA_SIZE >> sb->s_blocksize_bits; + reada_mask = reada_blocks - 1; + + fatent_init(&fatent); + lock_fat(sbi); + fatent_set_entry(&fatent, ent_start); + while (fatent.entry <= ent_end) { + /* readahead of fat blocks */ + if ((cur_block & reada_mask) == 0) { + unsigned long rest = sbi->fat_length - cur_block; + fat_ent_reada(sb, &fatent, min(reada_blocks, rest)); + } + cur_block++; + + err = fat_ent_read_block(sb, &fatent); + if (err) + goto error; + do { + if (ops->ent_get(&fatent) == FAT_ENT_FREE) { + free++; + } else if (free) { + if (free >= minlen) { + u32 clus = fatent.entry - free; + + err = fat_trim_clusters(sb, clus, free); + if (err && err != -EOPNOTSUPP) + goto error; + if (!err) + trimmed += free; + err = 0; + } + free = 0; + } + } while (fat_ent_next(sbi, &fatent) && fatent.entry <= ent_end); + + if (fatal_signal_pending(current)) { + err = -ERESTARTSYS; + goto error; + } + + if (need_resched()) { + fatent_brelse(&fatent); + unlock_fat(sbi); + cond_resched(); + lock_fat(sbi); + } + } + /* handle scenario when tail entries are all free */ + if (free && free >= minlen) { + u32 clus = fatent.entry - free; + + err = fat_trim_clusters(sb, clus, free); + if (err && err != -EOPNOTSUPP) + goto error; + if (!err) + trimmed += free; + err = 0; + } + +error: + fatent_brelse(&fatent); + unlock_fat(sbi); + + range->len = trimmed << sbi->cluster_bits; + + return err; +}