Description for this pull request:

- Clean-up unnecessary codes as ->valid_size is supported.
 - buffered-IO fallback is no longer needed when using direct-IO.
 - Move ->valid_size extension from mmap to ->page_mkwrite.
   This improves the overhead caused by unnecessary zero-out during mmap.
 - Fix memleaks from exfat_load_bitmap() and exfat_create_upcase_table().
 - Add sops->shutdown and ioctl.
 - Add Yuezhang Mo as a reviwer.
 -----BEGIN PGP SIGNATURE-----
 
 iQJKBAABCgA0FiEE6NzKS6Uv/XAAGHgyZwv7A1FEIQgFAmbytEQWHGxpbmtpbmpl
 b25Aa2VybmVsLm9yZwAKCRBnC/sDUUQhCEqUD/sEerRjBeNi+ivTvYtxqQGaDCnj
 Re6gBUt138rF2qyVcX3dP0wMHVNEHzjtdJjZGuQXAKttkZ1qW1wGbz0kyIyFjRfZ
 MHPaaqAavDiDFqxZnJvB9xKsuU6mb0Kr0JC6mKet3KD+Q2VekePSX+3SvwRDcPNb
 4CroYvJtOOWy21FKvKc2LxZBrowTElCPIhiXbHgWRhJBVhi4edrDo0391enzkKwt
 Is0/RzMbAsQ08Ap+TH6YIlPtA9aVSiTDyal1YaIgpXjaVxqF3MpMfPFG6+XJ8GOw
 k9BXM5XH5YXPZXallG8Fkx5Hh6Nrf9Vuvt68KbLQuzL6MdDEb8vTPEycQFHpapLx
 hk5TrL23Ok2RU/AJJXUDxii+J+3YzuTgIL6sdgJbaYb1ZYebiMzjRkwUJpH3dqg+
 lx1QtYWsVRR8fTtBEle1yVbOPcuyUWUkMpKVIUseVL0EiQNpiwBSGKKuus3Cul4O
 KA6Kx8hYEguHAIBn5U52mzIl9Ye+j+QyRmcmA/qnObk/1h+5FKn+HgnMINex0qmz
 PXzI+cLta6TZKtb8+KnTNImRXCDtcvtG9wkF25M3vmzBMiLfTnEZsXKwF+fPiydw
 +N19vX6HVT8JpIOGhbsRQp7abLR2IhYCeZQCWdT09Ol0VUsXx87+CfsLQpM3xw4U
 79nicqiwHjVP98Wjyg==
 =vVfO
 -----END PGP SIGNATURE-----

Merge tag 'exfat-for-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat

Pull exfat updates from Namjae Jeon:

 - Clean-up unnecessary codes as ->valid_size is supported

 - buffered-IO fallback is no longer needed when using direct-IO

 - Move ->valid_size extension from mmap to ->page_mkwrite. This
   improves the overhead caused by unnecessary zero-out during mmap.

 - Fix memleaks from exfat_load_bitmap() and exfat_create_upcase_table()

 - Add sops->shutdown and ioctl

 - Add Yuezhang Mo as a reviwer

* tag 'exfat-for-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat:
  MAINTAINERS: exfat: add myself as reviewer
  exfat: resolve memory leak from exfat_create_upcase_table()
  exfat: move extend valid_size into ->page_mkwrite()
  exfat: fix memory leak in exfat_load_bitmap()
  exfat: Implement sops->shutdown and ioctl
  exfat: do not fallback to buffered write
  exfat: drop ->i_size_ondisk
This commit is contained in:
Linus Torvalds 2024-09-24 15:26:04 -07:00
commit 4165cee7ec
9 changed files with 200 additions and 127 deletions

View File

@ -8466,6 +8466,7 @@ N: binfmt
EXFAT FILE SYSTEM
M: Namjae Jeon <linkinjeon@kernel.org>
M: Sungjong Seo <sj1557.seo@samsung.com>
R: Yuezhang Mo <yuezhang.mo@sony.com>
L: linux-fsdevel@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat.git

View File

@ -91,11 +91,8 @@ int exfat_load_bitmap(struct super_block *sb)
return -EIO;
type = exfat_get_entry_type(ep);
if (type == TYPE_UNUSED)
break;
if (type != TYPE_BITMAP)
continue;
if (ep->dentry.bitmap.flags == 0x0) {
if (type == TYPE_BITMAP &&
ep->dentry.bitmap.flags == 0x0) {
int err;
err = exfat_allocate_bitmap(sb, ep);
@ -103,6 +100,9 @@ int exfat_load_bitmap(struct super_block *sb)
return err;
}
brelse(bh);
if (type == TYPE_UNUSED)
return -EINVAL;
}
if (exfat_get_next_cluster(sb, &clu.dir))

View File

@ -10,6 +10,7 @@
#include <linux/ratelimit.h>
#include <linux/nls.h>
#include <linux/blkdev.h>
#include <uapi/linux/exfat.h>
#define EXFAT_ROOT_INO 1
@ -148,6 +149,9 @@ enum {
#define DIR_CACHE_SIZE \
(DIV_ROUND_UP(EXFAT_DEN_TO_B(ES_MAX_ENTRY_NUM), SECTOR_SIZE) + 1)
/* Superblock flags */
#define EXFAT_FLAGS_SHUTDOWN 1
struct exfat_dentry_namebuf {
char *lfn;
int lfnbuf_len; /* usually MAX_UNINAME_BUF_SIZE */
@ -267,6 +271,8 @@ struct exfat_sb_info {
unsigned int clu_srch_ptr; /* cluster search pointer */
unsigned int used_clusters; /* number of used clusters */
unsigned long s_exfat_flags; /* Exfat superblock flags */
struct mutex s_lock; /* superblock lock */
struct mutex bitmap_lock; /* bitmap lock */
struct exfat_mount_options options;
@ -309,13 +315,6 @@ struct exfat_inode_info {
/* for avoiding the race between alloc and free */
unsigned int cache_valid_id;
/*
* NOTE: i_size_ondisk is 64bits, so must hold ->inode_lock to access.
* physically allocated size.
*/
loff_t i_size_ondisk;
/* block-aligned i_size (used in cont_write_begin) */
loff_t i_size_aligned;
/* on-disk position of directory entry or 0 */
loff_t i_pos;
loff_t valid_size;
@ -338,6 +337,11 @@ static inline struct exfat_inode_info *EXFAT_I(struct inode *inode)
return container_of(inode, struct exfat_inode_info, vfs_inode);
}
static inline int exfat_forced_shutdown(struct super_block *sb)
{
return test_bit(EXFAT_FLAGS_SHUTDOWN, &EXFAT_SB(sb)->s_exfat_flags);
}
/*
* If ->i_mode can't hold 0222 (i.e. ATTR_RO), we use ->i_attrs to
* save ATTR_RO instead of ->i_mode.
@ -417,6 +421,11 @@ static inline bool is_valid_cluster(struct exfat_sb_info *sbi,
return clus >= EXFAT_FIRST_CLUSTER && clus < sbi->num_clusters;
}
static inline loff_t exfat_ondisk_size(const struct inode *inode)
{
return ((loff_t)inode->i_blocks) << 9;
}
/* super.c */
int exfat_set_volume_dirty(struct super_block *sb);
int exfat_clear_volume_dirty(struct super_block *sb);
@ -461,6 +470,7 @@ 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);
int exfat_force_shutdown(struct super_block *sb, u32 flags);
/* namei.c */
extern const struct dentry_operations exfat_dentry_ops;

View File

@ -29,7 +29,7 @@ static int exfat_cont_expand(struct inode *inode, loff_t size)
if (ret)
return ret;
num_clusters = EXFAT_B_TO_CLU_ROUND_UP(ei->i_size_ondisk, sbi);
num_clusters = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi);
new_num_clusters = EXFAT_B_TO_CLU_ROUND_UP(size, sbi);
if (new_num_clusters == num_clusters)
@ -74,8 +74,6 @@ static int exfat_cont_expand(struct inode *inode, loff_t size)
/* Expanded range not zeroed, do not update valid_size */
i_size_write(inode, size);
ei->i_size_aligned = round_up(size, sb->s_blocksize);
ei->i_size_ondisk = ei->i_size_aligned;
inode->i_blocks = round_up(size, sbi->cluster_size) >> 9;
mark_inode_dirty(inode);
@ -159,7 +157,7 @@ int __exfat_truncate(struct inode *inode)
exfat_set_volume_dirty(sb);
num_clusters_new = EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi);
num_clusters_phys = EXFAT_B_TO_CLU_ROUND_UP(ei->i_size_ondisk, sbi);
num_clusters_phys = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi);
exfat_chain_set(&clu, ei->start_clu, num_clusters_phys, ei->flags);
@ -245,8 +243,6 @@ void exfat_truncate(struct inode *inode)
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode);
unsigned int blocksize = i_blocksize(inode);
loff_t aligned_size;
int err;
mutex_lock(&sbi->s_lock);
@ -264,17 +260,6 @@ void exfat_truncate(struct inode *inode)
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9;
write_size:
aligned_size = i_size_read(inode);
if (aligned_size & (blocksize - 1)) {
aligned_size |= (blocksize - 1);
aligned_size++;
}
if (ei->i_size_ondisk > i_size_read(inode))
ei->i_size_ondisk = aligned_size;
if (ei->i_size_aligned > i_size_read(inode))
ei->i_size_aligned = aligned_size;
mutex_unlock(&sbi->s_lock);
}
@ -302,6 +287,9 @@ int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
unsigned int ia_valid;
int error;
if (unlikely(exfat_forced_shutdown(inode->i_sb)))
return -EIO;
if ((attr->ia_valid & ATTR_SIZE) &&
attr->ia_size > i_size_read(inode)) {
error = exfat_cont_expand(inode, attr->ia_size);
@ -485,6 +473,19 @@ static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg)
return 0;
}
static int exfat_ioctl_shutdown(struct super_block *sb, unsigned long arg)
{
u32 flags;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (get_user(flags, (__u32 __user *)arg))
return -EFAULT;
return exfat_force_shutdown(sb, flags);
}
long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct inode *inode = file_inode(filp);
@ -495,6 +496,8 @@ long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return exfat_ioctl_get_attributes(inode, user_attr);
case FAT_IOCTL_SET_ATTRIBUTES:
return exfat_ioctl_set_attributes(filp, user_attr);
case EXFAT_IOC_SHUTDOWN:
return exfat_ioctl_shutdown(inode->i_sb, arg);
case FITRIM:
return exfat_ioctl_fitrim(inode, arg);
default:
@ -515,6 +518,9 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
struct inode *inode = filp->f_mapping->host;
int err;
if (unlikely(exfat_forced_shutdown(inode->i_sb)))
return -EIO;
err = __generic_file_fsync(filp, start, end, datasync);
if (err)
return err;
@ -526,32 +532,32 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
return blkdev_issue_flush(inode->i_sb->s_bdev);
}
static int exfat_file_zeroed_range(struct file *file, loff_t start, loff_t end)
static int exfat_extend_valid_size(struct file *file, loff_t new_valid_size)
{
int err;
loff_t pos;
struct inode *inode = file_inode(file);
struct exfat_inode_info *ei = EXFAT_I(inode);
struct address_space *mapping = inode->i_mapping;
const struct address_space_operations *ops = mapping->a_ops;
while (start < end) {
u32 zerofrom, len;
pos = ei->valid_size;
while (pos < new_valid_size) {
u32 len;
struct folio *folio;
zerofrom = start & (PAGE_SIZE - 1);
len = PAGE_SIZE - zerofrom;
if (start + len > end)
len = end - start;
len = PAGE_SIZE - (pos & (PAGE_SIZE - 1));
if (pos + len > new_valid_size)
len = new_valid_size - pos;
err = ops->write_begin(file, mapping, start, len, &folio, NULL);
err = ops->write_begin(file, mapping, pos, len, &folio, NULL);
if (err)
goto out;
folio_zero_range(folio, offset_in_folio(folio, start), len);
err = ops->write_end(file, mapping, start, len, len, folio, NULL);
err = ops->write_end(file, mapping, pos, len, len, folio, NULL);
if (err < 0)
goto out;
start += len;
pos += len;
balance_dirty_pages_ratelimited(mapping);
cond_resched();
@ -579,7 +585,7 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
goto unlock;
if (pos > valid_size) {
ret = exfat_file_zeroed_range(file, valid_size, pos);
ret = exfat_extend_valid_size(file, pos);
if (ret < 0 && ret != -ENOSPC) {
exfat_err(inode->i_sb,
"write: fail to zero from %llu to %llu(%zd)",
@ -613,26 +619,46 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
return ret;
}
static int exfat_file_mmap(struct file *file, struct vm_area_struct *vma)
static vm_fault_t exfat_page_mkwrite(struct vm_fault *vmf)
{
int ret;
int err;
struct vm_area_struct *vma = vmf->vma;
struct file *file = vma->vm_file;
struct inode *inode = file_inode(file);
struct exfat_inode_info *ei = EXFAT_I(inode);
loff_t start = ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
loff_t end = min_t(loff_t, i_size_read(inode),
loff_t start, end;
if (!inode_trylock(inode))
return VM_FAULT_RETRY;
start = ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
end = min_t(loff_t, i_size_read(inode),
start + vma->vm_end - vma->vm_start);
if ((vma->vm_flags & VM_WRITE) && ei->valid_size < end) {
ret = exfat_file_zeroed_range(file, ei->valid_size, end);
if (ret < 0) {
exfat_err(inode->i_sb,
"mmap: fail to zero from %llu to %llu(%d)",
start, end, ret);
return ret;
if (ei->valid_size < end) {
err = exfat_extend_valid_size(file, end);
if (err < 0) {
inode_unlock(inode);
return vmf_fs_error(err);
}
}
return generic_file_mmap(file, vma);
inode_unlock(inode);
return filemap_page_mkwrite(vmf);
}
static const struct vm_operations_struct exfat_file_vm_ops = {
.fault = filemap_fault,
.map_pages = filemap_map_pages,
.page_mkwrite = exfat_page_mkwrite,
};
static int exfat_file_mmap(struct file *file, struct vm_area_struct *vma)
{
file_accessed(file);
vma->vm_ops = &exfat_file_vm_ops;
return 0;
}
const struct file_operations exfat_file_operations = {

View File

@ -102,6 +102,9 @@ int exfat_write_inode(struct inode *inode, struct writeback_control *wbc)
{
int ret;
if (unlikely(exfat_forced_shutdown(inode->i_sb)))
return -EIO;
mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock);
ret = __exfat_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock);
@ -130,11 +133,9 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode);
unsigned int local_clu_offset = clu_offset;
unsigned int num_to_be_allocated = 0, num_clusters = 0;
unsigned int num_to_be_allocated = 0, num_clusters;
if (ei->i_size_ondisk > 0)
num_clusters =
EXFAT_B_TO_CLU_ROUND_UP(ei->i_size_ondisk, sbi);
num_clusters = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi);
if (clu_offset >= num_clusters)
num_to_be_allocated = clu_offset - num_clusters + 1;
@ -260,21 +261,6 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
return 0;
}
static int exfat_map_new_buffer(struct exfat_inode_info *ei,
struct buffer_head *bh, loff_t pos)
{
if (buffer_delay(bh) && pos > ei->i_size_aligned)
return -EIO;
set_buffer_new(bh);
/*
* Adjust i_size_aligned if i_size_ondisk is bigger than it.
*/
if (ei->i_size_ondisk > ei->i_size_aligned)
ei->i_size_aligned = ei->i_size_ondisk;
return 0;
}
static int exfat_get_block(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create)
{
@ -288,7 +274,6 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
sector_t last_block;
sector_t phys = 0;
sector_t valid_blks;
loff_t pos;
mutex_lock(&sbi->s_lock);
last_block = EXFAT_B_TO_BLK_ROUND_UP(i_size_read(inode), sb);
@ -316,12 +301,6 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
mapped_blocks = sbi->sect_per_clus - sec_offset;
max_blocks = min(mapped_blocks, max_blocks);
pos = EXFAT_BLK_TO_B((iblock + 1), sb);
if ((create && iblock >= last_block) || buffer_delay(bh_result)) {
if (ei->i_size_ondisk < pos)
ei->i_size_ondisk = pos;
}
map_bh(bh_result, sb, phys);
if (buffer_delay(bh_result))
clear_buffer_delay(bh_result);
@ -342,13 +321,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
}
/* The area has not been written, map and mark as new. */
err = exfat_map_new_buffer(ei, bh_result, pos);
if (err) {
exfat_fs_error(sb,
"requested for bmap out of range(pos : (%llu) > i_size_aligned(%llu)\n",
pos, ei->i_size_aligned);
goto unlock_ret;
}
set_buffer_new(bh_result);
ei->valid_size = EXFAT_BLK_TO_B(iblock + max_blocks, sb);
mark_inode_dirty(inode);
@ -371,7 +344,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
* The block has been partially written,
* zero the unwritten part and map the block.
*/
loff_t size, off;
loff_t size, off, pos;
max_blocks = 1;
@ -382,7 +355,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
if (!bh_result->b_folio)
goto done;
pos -= sb->s_blocksize;
pos = EXFAT_BLK_TO_B(iblock, sb);
size = ei->valid_size - pos;
off = pos & (PAGE_SIZE - 1);
@ -432,6 +405,9 @@ static void exfat_readahead(struct readahead_control *rac)
static int exfat_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
if (unlikely(exfat_forced_shutdown(mapping->host->i_sb)))
return -EIO;
return mpage_writepages(mapping, wbc, exfat_get_block);
}
@ -452,6 +428,9 @@ static int exfat_write_begin(struct file *file, struct address_space *mapping,
{
int ret;
if (unlikely(exfat_forced_shutdown(mapping->host->i_sb)))
return -EIO;
ret = block_write_begin(mapping, pos, len, foliop, exfat_get_block);
if (ret < 0)
@ -469,14 +448,6 @@ static int exfat_write_end(struct file *file, struct address_space *mapping,
int err;
err = generic_write_end(file, mapping, pos, len, copied, folio, fsdata);
if (ei->i_size_aligned < i_size_read(inode)) {
exfat_fs_error(inode->i_sb,
"invalid size(size(%llu) > aligned(%llu)\n",
i_size_read(inode), ei->i_size_aligned);
return -EIO;
}
if (err < len)
exfat_write_failed(mapping, pos+len);
@ -504,20 +475,6 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
int rw = iov_iter_rw(iter);
ssize_t ret;
if (rw == WRITE) {
/*
* FIXME: blockdev_direct_IO() doesn't use ->write_begin(),
* so we need to update the ->i_size_aligned to block boundary.
*
* But we must fill the remaining area or hole by nul for
* updating ->i_size_aligned
*
* Return 0, and fallback to normal buffered write.
*/
if (EXFAT_I(inode)->i_size_aligned < size)
return 0;
}
/*
* Need to use the DIO_LOCKING for avoiding the race
* condition of exfat_get_block() and ->truncate().
@ -531,8 +488,18 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
} else
size = pos + ret;
/* zero the unwritten part in the partially written block */
if (rw == READ && pos < ei->valid_size && ei->valid_size < size) {
if (rw == WRITE) {
/*
* If the block had been partially written before this write,
* ->valid_size will not be updated in exfat_get_block(),
* update it here.
*/
if (ei->valid_size < size) {
ei->valid_size = size;
mark_inode_dirty(inode);
}
} else if (pos < ei->valid_size && ei->valid_size < size) {
/* zero the unwritten part in the partially written block */
iov_iter_revert(iter, size - ei->valid_size);
iov_iter_zero(size - ei->valid_size, iter);
}
@ -667,15 +634,6 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
i_size_write(inode, size);
/* ondisk and aligned size should be aligned with block size */
if (size & (inode->i_sb->s_blocksize - 1)) {
size |= (inode->i_sb->s_blocksize - 1);
size++;
}
ei->i_size_aligned = size;
ei->i_size_ondisk = size;
exfat_save_attr(inode, info->attr);
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9;

View File

@ -372,8 +372,6 @@ static int exfat_find_empty_entry(struct inode *inode,
/* directory inode should be updated in here */
i_size_write(inode, size);
ei->i_size_ondisk += sbi->cluster_size;
ei->i_size_aligned += sbi->cluster_size;
ei->valid_size += sbi->cluster_size;
ei->flags = p_dir->flags;
inode->i_blocks += sbi->cluster_size >> 9;
@ -549,6 +547,9 @@ static int exfat_create(struct mnt_idmap *idmap, struct inode *dir,
int err;
loff_t size = i_size_read(dir);
if (unlikely(exfat_forced_shutdown(sb)))
return -EIO;
mutex_lock(&EXFAT_SB(sb)->s_lock);
exfat_set_volume_dirty(sb);
err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_FILE,
@ -772,6 +773,9 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
struct exfat_entry_set_cache es;
int entry, err = 0;
if (unlikely(exfat_forced_shutdown(sb)))
return -EIO;
mutex_lock(&EXFAT_SB(sb)->s_lock);
exfat_chain_dup(&cdir, &ei->dir);
entry = ei->entry;
@ -825,6 +829,9 @@ static int exfat_mkdir(struct mnt_idmap *idmap, struct inode *dir,
int err;
loff_t size = i_size_read(dir);
if (unlikely(exfat_forced_shutdown(sb)))
return -EIO;
mutex_lock(&EXFAT_SB(sb)->s_lock);
exfat_set_volume_dirty(sb);
err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_DIR,
@ -915,6 +922,9 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
struct exfat_entry_set_cache es;
int entry, err;
if (unlikely(exfat_forced_shutdown(sb)))
return -EIO;
mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock);
exfat_chain_dup(&cdir, &ei->dir);
@ -982,6 +992,9 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
struct exfat_entry_set_cache old_es, new_es;
int sync = IS_DIRSYNC(inode);
if (unlikely(exfat_forced_shutdown(sb)))
return -EIO;
num_new_entries = exfat_calc_num_entries(p_uniname);
if (num_new_entries < 0)
return num_new_entries;

View File

@ -779,8 +779,11 @@ int exfat_create_upcase_table(struct super_block *sb)
le32_to_cpu(ep->dentry.upcase.checksum));
brelse(bh);
if (ret && ret != -EIO)
if (ret && ret != -EIO) {
/* free memory from exfat_load_upcase_table call */
exfat_free_upcase_table(sbi);
goto load_default;
}
/* load successfully */
return ret;

View File

@ -46,6 +46,9 @@ static int exfat_sync_fs(struct super_block *sb, int wait)
struct exfat_sb_info *sbi = EXFAT_SB(sb);
int err = 0;
if (unlikely(exfat_forced_shutdown(sb)))
return 0;
if (!wait)
return 0;
@ -167,6 +170,41 @@ static int exfat_show_options(struct seq_file *m, struct dentry *root)
return 0;
}
int exfat_force_shutdown(struct super_block *sb, u32 flags)
{
int ret;
struct exfat_sb_info *sbi = sb->s_fs_info;
struct exfat_mount_options *opts = &sbi->options;
if (exfat_forced_shutdown(sb))
return 0;
switch (flags) {
case EXFAT_GOING_DOWN_DEFAULT:
case EXFAT_GOING_DOWN_FULLSYNC:
ret = bdev_freeze(sb->s_bdev);
if (ret)
return ret;
bdev_thaw(sb->s_bdev);
set_bit(EXFAT_FLAGS_SHUTDOWN, &sbi->s_exfat_flags);
break;
case EXFAT_GOING_DOWN_NOSYNC:
set_bit(EXFAT_FLAGS_SHUTDOWN, &sbi->s_exfat_flags);
break;
default:
return -EINVAL;
}
if (opts->discard)
opts->discard = 0;
return 0;
}
static void exfat_shutdown(struct super_block *sb)
{
exfat_force_shutdown(sb, EXFAT_GOING_DOWN_NOSYNC);
}
static struct inode *exfat_alloc_inode(struct super_block *sb)
{
struct exfat_inode_info *ei;
@ -193,6 +231,7 @@ static const struct super_operations exfat_sops = {
.sync_fs = exfat_sync_fs,
.statfs = exfat_statfs,
.show_options = exfat_show_options,
.shutdown = exfat_shutdown,
};
enum {
@ -370,8 +409,6 @@ static int exfat_read_root(struct inode *inode)
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9;
ei->i_pos = ((loff_t)sbi->root_dir << 32) | 0xffffffff;
ei->i_size_aligned = i_size_read(inode);
ei->i_size_ondisk = i_size_read(inode);
exfat_save_attr(inode, EXFAT_ATTR_SUBDIR);
ei->i_crtime = simple_inode_init_ts(inode);

View File

@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
* Copyright (C) 2024 Unisoc Technologies Co., Ltd.
*/
#ifndef _UAPI_LINUX_EXFAT_H
#define _UAPI_LINUX_EXFAT_H
#include <linux/types.h>
#include <linux/ioctl.h>
/*
* exfat-specific ioctl commands
*/
#define EXFAT_IOC_SHUTDOWN _IOR('X', 125, __u32)
/*
* Flags used by EXFAT_IOC_SHUTDOWN
*/
#define EXFAT_GOING_DOWN_DEFAULT 0x0 /* default with full sync */
#define EXFAT_GOING_DOWN_FULLSYNC 0x1 /* going down with full sync*/
#define EXFAT_GOING_DOWN_NOSYNC 0x2 /* going down */
#endif /* _UAPI_LINUX_EXFAT_H */