Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exfat next misc #18

Open
wants to merge 5 commits into
base: exfat-next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,11 @@ script:
- sudo ./check generic/248
- sudo ./check generic/249
- sudo ./check generic/257
- sudo ./check generic/260
- sudo ./check generic/263
- sudo ./check generic/285
- sudo ./check generic/286
- sudo ./check generic/288
- sudo ./check generic/308
- sudo ./check generic/309
- sudo ./check generic/310
Expand Down
84 changes: 84 additions & 0 deletions balloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*/

#include <linux/version.h>
#include <linux/blkdev.h>
#include <linux/slab.h>
#include <linux/buffer_head.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
#include <linux/sched/signal.h>
#endif

#include "exfat_raw.h"
#include "exfat_fs.h"
Expand Down Expand Up @@ -264,3 +268,83 @@ int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count)
*ret_count = count;
return 0;
}

int exfat_trim_fs(struct inode *inode, struct fstrim_range *range)
{
unsigned int trim_begin, trim_end, count, next_free_clu;
u64 clu_start, clu_end, trim_minlen, trimmed_total = 0;
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
int err = 0;

clu_start = max_t(u64, range->start >> sbi->cluster_size_bits,
EXFAT_FIRST_CLUSTER);
clu_end = clu_start + (range->len >> sbi->cluster_size_bits) - 1;
trim_minlen = range->minlen >> sbi->cluster_size_bits;

if (clu_start >= sbi->num_clusters || range->len < sbi->cluster_size)
return -EINVAL;

if (clu_end >= sbi->num_clusters)
clu_end = sbi->num_clusters - 1;

mutex_lock(&sbi->bitmap_lock);

trim_begin = trim_end = exfat_find_free_bitmap(sb, clu_start);
if (trim_begin == EXFAT_EOF_CLUSTER)
goto unlock;

next_free_clu = exfat_find_free_bitmap(sb, trim_end + 1);
if (next_free_clu == EXFAT_EOF_CLUSTER)
goto unlock;

do {
if (next_free_clu == trim_end + 1) {
/* extend trim range for continuous free cluster */
trim_end++;
} else {
/* trim current range if it's larger than trim_minlen */
count = trim_end - trim_begin + 1;
if (count >= trim_minlen) {
err = sb_issue_discard(sb,
exfat_cluster_to_sector(sbi, trim_begin),
count * sbi->sect_per_clus, GFP_NOFS, 0);
if (err)
goto unlock;

trimmed_total += count;
}

/* set next start point of the free hole */
trim_begin = trim_end = next_free_clu;
}

if (next_free_clu >= clu_end)
break;

if (fatal_signal_pending(current)) {
err = -ERESTARTSYS;
goto unlock;
}

next_free_clu = exfat_find_free_bitmap(sb, next_free_clu + 1);
} while (next_free_clu != EXFAT_EOF_CLUSTER &&
next_free_clu > trim_end);

/* try to trim remainder */
count = trim_end - trim_begin + 1;
if (count >= trim_minlen) {
err = sb_issue_discard(sb, exfat_cluster_to_sector(sbi, trim_begin),
count * sbi->sect_per_clus, GFP_NOFS, 0);
if (err)
goto unlock;

trimmed_total += count;
}

unlock:
mutex_unlock(&sbi->bitmap_lock);
range->len = trimmed_total << sbi->cluster_size_bits;

return err;
}
5 changes: 5 additions & 0 deletions dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <linux/version.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/bio.h>
#include <linux/buffer_head.h>

Expand Down Expand Up @@ -307,6 +308,10 @@ const struct file_operations exfat_dir_operations = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
.iterate = exfat_iterate,
.unlocked_ioctl = exfat_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = exfat_compat_ioctl,
#endif
.fsync = exfat_file_fsync,
};

Expand Down
9 changes: 7 additions & 2 deletions exfat_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ enum {
#define FAT_ENT_SIZE (4)
#define FAT_ENT_SIZE_BITS (2)
#define FAT_ENT_OFFSET_SECTOR(sb, loc) (EXFAT_SB(sb)->FAT1_start_sector + \
(((u64)loc << FAT_ENT_SIZE_BITS) >> sb->s_blocksize_bits))
(((u64)(loc) << FAT_ENT_SIZE_BITS) >> sb->s_blocksize_bits))
#define FAT_ENT_OFFSET_BYTE_IN_SECTOR(sb, loc) \
((loc << FAT_ENT_SIZE_BITS) & (sb->s_blocksize - 1))
(((loc) << FAT_ENT_SIZE_BITS) & (sb->s_blocksize - 1))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no description in patch why you add () in macro.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am doing some patches, to do some optimization.
If I use FAT_ENT_OFFSET_BYTE_IN_SECTOR with an expression, it fails.
I will update the patch.


/*
* helpers for bitmap.
Expand Down Expand Up @@ -247,6 +247,7 @@ struct exfat_sb_info {
unsigned int used_clusters; /* number of used clusters */

struct mutex s_lock; /* superblock lock */
struct mutex bitmap_lock; /* bitmap lock */
struct exfat_mount_options options;
struct nls_table *nls_io; /* Charset used for input and display */
struct ratelimit_state ratelimit;
Expand Down Expand Up @@ -424,6 +425,7 @@ int exfat_set_bitmap(struct inode *inode, unsigned int clu);
void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync);
unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu);
int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count);
int exfat_trim_fs(struct inode *inode, struct fstrim_range *range);

/* file.c */
extern const struct file_operations exfat_file_operations;
Expand All @@ -438,6 +440,9 @@ int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat);
#endif
int exfat_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
long exfat_compat_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg);


/* namei.c */
Expand Down
60 changes: 44 additions & 16 deletions fatent.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,28 @@ static int __exfat_ent_get(struct super_block *sb, unsigned int loc,
return 0;
}

static inline bool is_valid_cluster(struct exfat_sb_info *sbi,
unsigned int clus)
{
if (clus < EXFAT_FIRST_CLUSTER || sbi->num_clusters <= clus)
return false;
return true;
}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add patch description why this patch is needed. and if you think this patch is needed from exfat in kernel mainline, Can you send the patch to LKML ?

int exfat_ent_set(struct super_block *sb, unsigned int loc,
unsigned int content)
{
unsigned int off;
sector_t sec;
__le32 *fat_entry;
struct buffer_head *bh;
struct exfat_sb_info *sbi = EXFAT_SB(sb);

if (!is_valid_cluster(sbi, loc)) {
exfat_fs_error(sb, "invalid write to FAT (entry 0x%08x)",
loc);
return -EIO;
}

sec = FAT_ENT_OFFSET_SECTOR(sb, loc);
off = FAT_ENT_OFFSET_BYTE_IN_SECTOR(sb, loc);
Expand All @@ -89,14 +104,6 @@ int exfat_ent_set(struct super_block *sb, unsigned int loc,
return 0;
}

static inline bool is_valid_cluster(struct exfat_sb_info *sbi,
unsigned int clus)
{
if (clus < EXFAT_FIRST_CLUSTER || sbi->num_clusters <= clus)
return false;
return true;
}

int exfat_ent_get(struct super_block *sb, unsigned int loc,
unsigned int *content)
{
Expand Down Expand Up @@ -159,13 +166,14 @@ int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain,
return 0;
}

int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain)
/* This function must be called with bitmap_lock held */
static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain)
{
unsigned int num_clusters = 0;
unsigned int clu;
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
int cur_cmap_i, next_cmap_i;
unsigned int num_clusters = 0;
unsigned int clu;

/* invalid cluster number */
if (p_chain->dir == EXFAT_FREE_CLUSTER ||
Expand Down Expand Up @@ -238,6 +246,17 @@ int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain)
return 0;
}

int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain)
{
int ret = 0;

mutex_lock(&EXFAT_SB(inode->i_sb)->bitmap_lock);
ret = __exfat_free_cluster(inode, p_chain);
mutex_unlock(&EXFAT_SB(inode->i_sb)->bitmap_lock);

return ret;
}

int exfat_find_last_cluster(struct super_block *sb, struct exfat_chain *p_chain,
unsigned int *ret_clu)
{
Expand Down Expand Up @@ -336,6 +355,8 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
if (num_alloc > total_cnt - sbi->used_clusters)
return -ENOSPC;

mutex_lock(&sbi->bitmap_lock);

hint_clu = p_chain->dir;
/* find new cluster */
if (hint_clu == EXFAT_EOF_CLUSTER) {
Expand All @@ -346,8 +367,10 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
}

hint_clu = exfat_find_free_bitmap(sb, sbi->clu_srch_ptr);
if (hint_clu == EXFAT_EOF_CLUSTER)
return -ENOSPC;
if (hint_clu == EXFAT_EOF_CLUSTER) {
ret = -ENOSPC;
goto unlock;
}
}

/* check cluster validation */
Expand All @@ -357,8 +380,10 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
hint_clu = EXFAT_FIRST_CLUSTER;
if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {
if (exfat_chain_cont_cluster(sb, p_chain->dir,
num_clusters))
return -EIO;
num_clusters)) {
ret = -EIO;
goto unlock;
}
p_chain->flags = ALLOC_FAT_CHAIN;
}
}
Expand Down Expand Up @@ -408,6 +433,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
sbi->used_clusters += num_clusters;

p_chain->size += num_clusters;
mutex_unlock(&sbi->bitmap_lock);
return 0;
}

Expand All @@ -427,7 +453,9 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
}
free_cluster:
if (num_clusters)
exfat_free_cluster(inode, p_chain);
__exfat_free_cluster(inode, p_chain);
unlock:
mutex_unlock(&sbi->bitmap_lock);
return ret;
}

Expand Down
55 changes: 53 additions & 2 deletions file.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
#include <linux/buffer_head.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/cred.h>
#else
#include <linux/compat.h>
#endif
#include <linux/compat.h>
#include <linux/blkdev.h>

#include "exfat_raw.h"
Expand Down Expand Up @@ -391,6 +390,54 @@ int exfat_setattr(struct dentry *dentry, struct iattr *attr)
return error;
}

static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg)
{
struct request_queue *q = bdev_get_queue(inode->i_sb->s_bdev);
struct fstrim_range range;
int ret = 0;

if (!capable(CAP_SYS_ADMIN))
return -EPERM;

if (!blk_queue_discard(q))
return -EOPNOTSUPP;

if (copy_from_user(&range, (struct fstrim_range __user *)arg, sizeof(range)))
return -EFAULT;

range.minlen = max_t(unsigned int, range.minlen,
q->limits.discard_granularity);

ret = exfat_trim_fs(inode, &range);
if (ret < 0)
return ret;

if (copy_to_user((struct fstrim_range __user *)arg, &range, sizeof(range)))
return -EFAULT;

return 0;
}

long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct inode *inode = file_inode(filp);

switch (cmd) {
case FITRIM:
return exfat_ioctl_fitrim(inode, arg);
default:
return -ENOTTY;
}
}

#ifdef CONFIG_COMPAT
long exfat_compat_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
return exfat_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
}
#endif

int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
{
struct inode *inode = filp->f_mapping->host;
Expand All @@ -415,6 +462,10 @@ const struct file_operations exfat_file_operations = {
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
.write_iter = generic_file_write_iter,
.unlocked_ioctl = exfat_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = exfat_compat_ioctl,
#endif
.mmap = generic_file_mmap,
.fsync = exfat_file_fsync,
.splice_read = generic_file_splice_read,
Expand Down
1 change: 1 addition & 0 deletions super.c
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,7 @@ static int exfat_init_fs_context(struct fs_context *fc)
return -ENOMEM;

mutex_init(&sbi->s_lock);
mutex_init(&sbi->bitmap_lock);
ratelimit_state_init(&sbi->ratelimit, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);

Expand Down