fat: add FITRIM ioctl for FAT file system
Add FITRIM ioctl for FAT file system [witallwang@gmail.com: use u64s] Link: http://lkml.kernel.org/r/87h8l37hub.fsf@mail.parknet.co.jp [hirofumi@mail.parknet.co.jp: bug fixes, coding style fixes, add signal check] Link: http://lkml.kernel.org/r/87fu10anhj.fsf@mail.parknet.co.jp Signed-off-by: Wentao Wang <witallwang@gmail.com> Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
a13f085d11
commit
f663b5b38f
3 changed files with 136 additions and 0 deletions
|
@ -357,6 +357,7 @@ extern int fat_alloc_clusters(struct inode *inode, int *cluster,
|
|||
int nr_cluster);
|
||||
extern int fat_free_clusters(struct inode *inode, int cluster);
|
||||
extern int fat_count_free_clusters(struct super_block *sb);
|
||||
extern int fat_trim_fs(struct inode *inode, struct fstrim_range *range);
|
||||
|
||||
/* fat/file.c */
|
||||
extern long fat_generic_ioctl(struct file *filp, unsigned int cmd,
|
||||
|
|
102
fs/fat/fatent.c
102
fs/fat/fatent.c
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include "fat.h"
|
||||
|
||||
struct fatent_operations {
|
||||
|
@ -690,3 +691,104 @@ int fat_count_free_clusters(struct super_block *sb)
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -121,6 +121,37 @@ static int fat_ioctl_get_volume_id(struct inode *inode, u32 __user *user_attr)
|
|||
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 +164,8 @@ long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
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 */
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue