forked from luck/tmp_suning_uos_patched
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
|
@ -357,6 +357,7 @@ extern int fat_alloc_clusters(struct inode *inode, int *cluster,
|
||||||
int nr_cluster);
|
int nr_cluster);
|
||||||
extern int fat_free_clusters(struct inode *inode, int cluster);
|
extern int fat_free_clusters(struct inode *inode, int cluster);
|
||||||
extern int fat_count_free_clusters(struct super_block *sb);
|
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 */
|
/* fat/file.c */
|
||||||
extern long fat_generic_ioctl(struct file *filp, unsigned int cmd,
|
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/blkdev.h>
|
||||||
|
#include <linux/sched/signal.h>
|
||||||
#include "fat.h"
|
#include "fat.h"
|
||||||
|
|
||||||
struct fatent_operations {
|
struct fatent_operations {
|
||||||
|
@ -690,3 +691,104 @@ int fat_count_free_clusters(struct super_block *sb)
|
||||||
unlock_fat(sbi);
|
unlock_fat(sbi);
|
||||||
return err;
|
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);
|
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)
|
long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||||
{
|
{
|
||||||
struct inode *inode = file_inode(filp);
|
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);
|
return fat_ioctl_set_attributes(filp, user_attr);
|
||||||
case FAT_IOCTL_GET_VOLUME_ID:
|
case FAT_IOCTL_GET_VOLUME_ID:
|
||||||
return fat_ioctl_get_volume_id(inode, user_attr);
|
return fat_ioctl_get_volume_id(inode, user_attr);
|
||||||
|
case FITRIM:
|
||||||
|
return fat_ioctl_fitrim(inode, arg);
|
||||||
default:
|
default:
|
||||||
return -ENOTTY; /* Inappropriate ioctl for device */
|
return -ENOTTY; /* Inappropriate ioctl for device */
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user