2020-02-03 07:47:19 -05:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
/*
|
|
|
|
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <asm/unaligned.h>
|
|
|
|
#include <linux/buffer_head.h>
|
2022-04-08 09:14:06 -04:00
|
|
|
#include <linux/blkdev.h>
|
2020-02-03 07:47:19 -05:00
|
|
|
|
|
|
|
#include "exfat_raw.h"
|
|
|
|
#include "exfat_fs.h"
|
|
|
|
|
|
|
|
static int exfat_mirror_bh(struct super_block *sb, sector_t sec,
|
|
|
|
struct buffer_head *bh)
|
|
|
|
{
|
|
|
|
struct buffer_head *c_bh;
|
|
|
|
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
|
|
|
sector_t sec2;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
if (sbi->FAT2_start_sector != sbi->FAT1_start_sector) {
|
|
|
|
sec2 = sec - sbi->FAT1_start_sector + sbi->FAT2_start_sector;
|
|
|
|
c_bh = sb_getblk(sb, sec2);
|
|
|
|
if (!c_bh)
|
|
|
|
return -ENOMEM;
|
|
|
|
memcpy(c_bh->b_data, bh->b_data, sb->s_blocksize);
|
|
|
|
set_buffer_uptodate(c_bh);
|
|
|
|
mark_buffer_dirty(c_bh);
|
2020-02-08 05:13:34 -05:00
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
|
2020-02-03 07:47:19 -05:00
|
|
|
if (sb->s_flags & SB_SYNCHRONOUS)
|
2020-02-08 05:13:34 -05:00
|
|
|
#else
|
|
|
|
if (sb->s_flags & MS_SYNCHRONOUS)
|
|
|
|
#endif
|
2020-02-03 07:47:19 -05:00
|
|
|
err = sync_dirty_buffer(c_bh);
|
|
|
|
brelse(c_bh);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __exfat_ent_get(struct super_block *sb, unsigned int loc,
|
|
|
|
unsigned int *content)
|
|
|
|
{
|
|
|
|
unsigned int off;
|
|
|
|
sector_t sec;
|
|
|
|
struct buffer_head *bh;
|
|
|
|
|
|
|
|
sec = FAT_ENT_OFFSET_SECTOR(sb, loc);
|
|
|
|
off = FAT_ENT_OFFSET_BYTE_IN_SECTOR(sb, loc);
|
|
|
|
|
|
|
|
bh = sb_bread(sb, sec);
|
|
|
|
if (!bh)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
*content = le32_to_cpu(*(__le32 *)(&bh->b_data[off]));
|
|
|
|
|
|
|
|
/* remap reserved clusters to simplify code */
|
|
|
|
if (*content > EXFAT_BAD_CLUSTER)
|
|
|
|
*content = EXFAT_EOF_CLUSTER;
|
|
|
|
|
|
|
|
brelse(bh);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
sec = FAT_ENT_OFFSET_SECTOR(sb, loc);
|
|
|
|
off = FAT_ENT_OFFSET_BYTE_IN_SECTOR(sb, loc);
|
|
|
|
|
|
|
|
bh = sb_bread(sb, sec);
|
|
|
|
if (!bh)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
fat_entry = (__le32 *)&(bh->b_data[off]);
|
|
|
|
*fat_entry = cpu_to_le32(content);
|
2020-02-08 05:13:34 -05:00
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
|
2020-06-16 01:25:07 -04:00
|
|
|
exfat_update_bh(bh, sb->s_flags & SB_SYNCHRONOUS);
|
2020-02-08 05:13:34 -05:00
|
|
|
#else
|
2020-06-16 01:25:07 -04:00
|
|
|
exfat_update_bh(bh, sb->s_flags & MS_SYNCHRONOUS);
|
2020-02-08 05:13:34 -05:00
|
|
|
#endif
|
2020-02-03 07:47:19 -05:00
|
|
|
exfat_mirror_bh(sb, sec, bh);
|
|
|
|
brelse(bh);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int exfat_ent_get(struct super_block *sb, unsigned int loc,
|
|
|
|
unsigned int *content)
|
|
|
|
{
|
|
|
|
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!is_valid_cluster(sbi, loc)) {
|
|
|
|
exfat_fs_error(sb, "invalid access to FAT (entry 0x%08x)",
|
|
|
|
loc);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = __exfat_ent_get(sb, loc, content);
|
|
|
|
if (err) {
|
|
|
|
exfat_fs_error(sb,
|
|
|
|
"failed to access to FAT (entry 0x%08x, err:%d)",
|
|
|
|
loc, err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*content == EXFAT_FREE_CLUSTER) {
|
|
|
|
exfat_fs_error(sb,
|
|
|
|
"invalid access to FAT free cluster (entry 0x%08x)",
|
|
|
|
loc);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*content == EXFAT_BAD_CLUSTER) {
|
|
|
|
exfat_fs_error(sb,
|
|
|
|
"invalid access to FAT bad cluster (entry 0x%08x)",
|
|
|
|
loc);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*content != EXFAT_EOF_CLUSTER && !is_valid_cluster(sbi, *content)) {
|
|
|
|
exfat_fs_error(sb,
|
|
|
|
"invalid access to FAT (entry 0x%08x) bogus content (0x%08x)",
|
|
|
|
loc, *content);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain,
|
|
|
|
unsigned int len)
|
|
|
|
{
|
|
|
|
if (!len)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while (len > 1) {
|
|
|
|
if (exfat_ent_set(sb, chain, chain + 1))
|
|
|
|
return -EIO;
|
|
|
|
chain++;
|
|
|
|
len--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (exfat_ent_set(sb, chain, EXFAT_EOF_CLUSTER))
|
|
|
|
return -EIO;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-24 19:09:50 -04:00
|
|
|
/* This function must be called with bitmap_lock held */
|
|
|
|
static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain)
|
2020-02-03 07:47:19 -05:00
|
|
|
{
|
|
|
|
struct super_block *sb = inode->i_sb;
|
|
|
|
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
exfat: improve performance of exfat_free_cluster when using dirsync mount option
There are stressful update of cluster allocation bitmap when using
dirsync mount option which is doing sync buffer on every cluster bit
clearing. This could result in performance degradation when deleting
big size file.
Fix to update only when the bitmap buffer index is changed would make
less disk access, improving performance especially for truncate operation.
Testing with Samsung 256GB sdcard, mounted with dirsync option
(mount -t exfat /dev/block/mmcblk0p1 /temp/mount -o dirsync)
Remove 4GB file, blktrace result.
[Before] : 39 secs.
Total (blktrace):
Reads Queued: 0, 0KiB Writes Queued: 32775, 16387KiB
Read Dispatches: 0, 0KiB Write Dispatches: 32775, 16387KiB
Reads Requeued: 0 Writes Requeued: 0
Reads Completed: 0, 0KiB Writes Completed: 32775, 16387KiB
Read Merges: 0, 0KiB Write Merges: 0, 0KiB
IO unplugs: 2 Timer unplugs: 0
[After] : 1 sec.
Total (blktrace):
Reads Queued: 0, 0KiB Writes Queued: 13, 6KiB
Read Dispatches: 0, 0KiB Write Dispatches: 13, 6KiB
Reads Requeued: 0 Writes Requeued: 0
Reads Completed: 0, 0KiB Writes Completed: 13, 6KiB
Read Merges: 0, 0KiB Write Merges: 0, 0KiB
IO unplugs: 1 Timer unplugs: 0
Signed-off-by: Hyeongseok Kim <hyeongseok@gmail.com>
Acked-by: Sungjong Seo <sj1557.seo@samsung.com>
Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com>
2021-01-31 20:09:48 -05:00
|
|
|
int cur_cmap_i, next_cmap_i;
|
2021-03-24 19:09:50 -04:00
|
|
|
unsigned int num_clusters = 0;
|
|
|
|
unsigned int clu;
|
2020-02-03 07:47:19 -05:00
|
|
|
|
|
|
|
/* invalid cluster number */
|
|
|
|
if (p_chain->dir == EXFAT_FREE_CLUSTER ||
|
|
|
|
p_chain->dir == EXFAT_EOF_CLUSTER ||
|
|
|
|
p_chain->dir < EXFAT_FIRST_CLUSTER)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* no cluster to truncate */
|
|
|
|
if (p_chain->size == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* check cluster validation */
|
2020-06-04 02:45:12 -04:00
|
|
|
if (!is_valid_cluster(sbi, p_chain->dir)) {
|
2020-04-05 23:49:36 -04:00
|
|
|
exfat_err(sb, "invalid start cluster (%u)", p_chain->dir);
|
2020-02-03 07:47:19 -05:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
clu = p_chain->dir;
|
|
|
|
|
exfat: improve performance of exfat_free_cluster when using dirsync mount option
There are stressful update of cluster allocation bitmap when using
dirsync mount option which is doing sync buffer on every cluster bit
clearing. This could result in performance degradation when deleting
big size file.
Fix to update only when the bitmap buffer index is changed would make
less disk access, improving performance especially for truncate operation.
Testing with Samsung 256GB sdcard, mounted with dirsync option
(mount -t exfat /dev/block/mmcblk0p1 /temp/mount -o dirsync)
Remove 4GB file, blktrace result.
[Before] : 39 secs.
Total (blktrace):
Reads Queued: 0, 0KiB Writes Queued: 32775, 16387KiB
Read Dispatches: 0, 0KiB Write Dispatches: 32775, 16387KiB
Reads Requeued: 0 Writes Requeued: 0
Reads Completed: 0, 0KiB Writes Completed: 32775, 16387KiB
Read Merges: 0, 0KiB Write Merges: 0, 0KiB
IO unplugs: 2 Timer unplugs: 0
[After] : 1 sec.
Total (blktrace):
Reads Queued: 0, 0KiB Writes Queued: 13, 6KiB
Read Dispatches: 0, 0KiB Write Dispatches: 13, 6KiB
Reads Requeued: 0 Writes Requeued: 0
Reads Completed: 0, 0KiB Writes Completed: 13, 6KiB
Read Merges: 0, 0KiB Write Merges: 0, 0KiB
IO unplugs: 1 Timer unplugs: 0
Signed-off-by: Hyeongseok Kim <hyeongseok@gmail.com>
Acked-by: Sungjong Seo <sj1557.seo@samsung.com>
Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com>
2021-01-31 20:09:48 -05:00
|
|
|
cur_cmap_i = next_cmap_i =
|
|
|
|
BITMAP_OFFSET_SECTOR_INDEX(sb, CLUSTER_TO_BITMAP_ENT(clu));
|
|
|
|
|
2020-02-03 07:47:19 -05:00
|
|
|
if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {
|
exfat: improve performance of exfat_free_cluster when using dirsync mount option
There are stressful update of cluster allocation bitmap when using
dirsync mount option which is doing sync buffer on every cluster bit
clearing. This could result in performance degradation when deleting
big size file.
Fix to update only when the bitmap buffer index is changed would make
less disk access, improving performance especially for truncate operation.
Testing with Samsung 256GB sdcard, mounted with dirsync option
(mount -t exfat /dev/block/mmcblk0p1 /temp/mount -o dirsync)
Remove 4GB file, blktrace result.
[Before] : 39 secs.
Total (blktrace):
Reads Queued: 0, 0KiB Writes Queued: 32775, 16387KiB
Read Dispatches: 0, 0KiB Write Dispatches: 32775, 16387KiB
Reads Requeued: 0 Writes Requeued: 0
Reads Completed: 0, 0KiB Writes Completed: 32775, 16387KiB
Read Merges: 0, 0KiB Write Merges: 0, 0KiB
IO unplugs: 2 Timer unplugs: 0
[After] : 1 sec.
Total (blktrace):
Reads Queued: 0, 0KiB Writes Queued: 13, 6KiB
Read Dispatches: 0, 0KiB Write Dispatches: 13, 6KiB
Reads Requeued: 0 Writes Requeued: 0
Reads Completed: 0, 0KiB Writes Completed: 13, 6KiB
Read Merges: 0, 0KiB Write Merges: 0, 0KiB
IO unplugs: 1 Timer unplugs: 0
Signed-off-by: Hyeongseok Kim <hyeongseok@gmail.com>
Acked-by: Sungjong Seo <sj1557.seo@samsung.com>
Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com>
2021-01-31 20:09:48 -05:00
|
|
|
unsigned int last_cluster = p_chain->dir + p_chain->size - 1;
|
2020-02-03 07:47:19 -05:00
|
|
|
do {
|
exfat: improve performance of exfat_free_cluster when using dirsync mount option
There are stressful update of cluster allocation bitmap when using
dirsync mount option which is doing sync buffer on every cluster bit
clearing. This could result in performance degradation when deleting
big size file.
Fix to update only when the bitmap buffer index is changed would make
less disk access, improving performance especially for truncate operation.
Testing with Samsung 256GB sdcard, mounted with dirsync option
(mount -t exfat /dev/block/mmcblk0p1 /temp/mount -o dirsync)
Remove 4GB file, blktrace result.
[Before] : 39 secs.
Total (blktrace):
Reads Queued: 0, 0KiB Writes Queued: 32775, 16387KiB
Read Dispatches: 0, 0KiB Write Dispatches: 32775, 16387KiB
Reads Requeued: 0 Writes Requeued: 0
Reads Completed: 0, 0KiB Writes Completed: 32775, 16387KiB
Read Merges: 0, 0KiB Write Merges: 0, 0KiB
IO unplugs: 2 Timer unplugs: 0
[After] : 1 sec.
Total (blktrace):
Reads Queued: 0, 0KiB Writes Queued: 13, 6KiB
Read Dispatches: 0, 0KiB Write Dispatches: 13, 6KiB
Reads Requeued: 0 Writes Requeued: 0
Reads Completed: 0, 0KiB Writes Completed: 13, 6KiB
Read Merges: 0, 0KiB Write Merges: 0, 0KiB
IO unplugs: 1 Timer unplugs: 0
Signed-off-by: Hyeongseok Kim <hyeongseok@gmail.com>
Acked-by: Sungjong Seo <sj1557.seo@samsung.com>
Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com>
2021-01-31 20:09:48 -05:00
|
|
|
bool sync = false;
|
|
|
|
|
|
|
|
if (clu < last_cluster)
|
|
|
|
next_cmap_i =
|
|
|
|
BITMAP_OFFSET_SECTOR_INDEX(sb, CLUSTER_TO_BITMAP_ENT(clu+1));
|
|
|
|
|
|
|
|
/* flush bitmap only if index would be changed or for last cluster */
|
|
|
|
if (clu == last_cluster || cur_cmap_i != next_cmap_i) {
|
|
|
|
sync = true;
|
|
|
|
cur_cmap_i = next_cmap_i;
|
|
|
|
}
|
2020-02-03 07:47:19 -05:00
|
|
|
|
exfat: improve performance of exfat_free_cluster when using dirsync mount option
There are stressful update of cluster allocation bitmap when using
dirsync mount option which is doing sync buffer on every cluster bit
clearing. This could result in performance degradation when deleting
big size file.
Fix to update only when the bitmap buffer index is changed would make
less disk access, improving performance especially for truncate operation.
Testing with Samsung 256GB sdcard, mounted with dirsync option
(mount -t exfat /dev/block/mmcblk0p1 /temp/mount -o dirsync)
Remove 4GB file, blktrace result.
[Before] : 39 secs.
Total (blktrace):
Reads Queued: 0, 0KiB Writes Queued: 32775, 16387KiB
Read Dispatches: 0, 0KiB Write Dispatches: 32775, 16387KiB
Reads Requeued: 0 Writes Requeued: 0
Reads Completed: 0, 0KiB Writes Completed: 32775, 16387KiB
Read Merges: 0, 0KiB Write Merges: 0, 0KiB
IO unplugs: 2 Timer unplugs: 0
[After] : 1 sec.
Total (blktrace):
Reads Queued: 0, 0KiB Writes Queued: 13, 6KiB
Read Dispatches: 0, 0KiB Write Dispatches: 13, 6KiB
Reads Requeued: 0 Writes Requeued: 0
Reads Completed: 0, 0KiB Writes Completed: 13, 6KiB
Read Merges: 0, 0KiB Write Merges: 0, 0KiB
IO unplugs: 1 Timer unplugs: 0
Signed-off-by: Hyeongseok Kim <hyeongseok@gmail.com>
Acked-by: Sungjong Seo <sj1557.seo@samsung.com>
Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com>
2021-01-31 20:09:48 -05:00
|
|
|
exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode)));
|
|
|
|
clu++;
|
2020-02-03 07:47:19 -05:00
|
|
|
num_clusters++;
|
|
|
|
} while (num_clusters < p_chain->size);
|
|
|
|
} else {
|
|
|
|
do {
|
exfat: improve performance of exfat_free_cluster when using dirsync mount option
There are stressful update of cluster allocation bitmap when using
dirsync mount option which is doing sync buffer on every cluster bit
clearing. This could result in performance degradation when deleting
big size file.
Fix to update only when the bitmap buffer index is changed would make
less disk access, improving performance especially for truncate operation.
Testing with Samsung 256GB sdcard, mounted with dirsync option
(mount -t exfat /dev/block/mmcblk0p1 /temp/mount -o dirsync)
Remove 4GB file, blktrace result.
[Before] : 39 secs.
Total (blktrace):
Reads Queued: 0, 0KiB Writes Queued: 32775, 16387KiB
Read Dispatches: 0, 0KiB Write Dispatches: 32775, 16387KiB
Reads Requeued: 0 Writes Requeued: 0
Reads Completed: 0, 0KiB Writes Completed: 32775, 16387KiB
Read Merges: 0, 0KiB Write Merges: 0, 0KiB
IO unplugs: 2 Timer unplugs: 0
[After] : 1 sec.
Total (blktrace):
Reads Queued: 0, 0KiB Writes Queued: 13, 6KiB
Read Dispatches: 0, 0KiB Write Dispatches: 13, 6KiB
Reads Requeued: 0 Writes Requeued: 0
Reads Completed: 0, 0KiB Writes Completed: 13, 6KiB
Read Merges: 0, 0KiB Write Merges: 0, 0KiB
IO unplugs: 1 Timer unplugs: 0
Signed-off-by: Hyeongseok Kim <hyeongseok@gmail.com>
Acked-by: Sungjong Seo <sj1557.seo@samsung.com>
Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com>
2021-01-31 20:09:48 -05:00
|
|
|
bool sync = false;
|
|
|
|
unsigned int n_clu = clu;
|
|
|
|
int err = exfat_get_next_cluster(sb, &n_clu);
|
|
|
|
|
|
|
|
if (err || n_clu == EXFAT_EOF_CLUSTER)
|
|
|
|
sync = true;
|
|
|
|
else
|
|
|
|
next_cmap_i =
|
|
|
|
BITMAP_OFFSET_SECTOR_INDEX(sb, CLUSTER_TO_BITMAP_ENT(n_clu));
|
|
|
|
|
|
|
|
if (cur_cmap_i != next_cmap_i) {
|
|
|
|
sync = true;
|
|
|
|
cur_cmap_i = next_cmap_i;
|
|
|
|
}
|
|
|
|
|
|
|
|
exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode)));
|
|
|
|
clu = n_clu;
|
|
|
|
num_clusters++;
|
2020-02-03 07:47:19 -05:00
|
|
|
|
exfat: improve performance of exfat_free_cluster when using dirsync mount option
There are stressful update of cluster allocation bitmap when using
dirsync mount option which is doing sync buffer on every cluster bit
clearing. This could result in performance degradation when deleting
big size file.
Fix to update only when the bitmap buffer index is changed would make
less disk access, improving performance especially for truncate operation.
Testing with Samsung 256GB sdcard, mounted with dirsync option
(mount -t exfat /dev/block/mmcblk0p1 /temp/mount -o dirsync)
Remove 4GB file, blktrace result.
[Before] : 39 secs.
Total (blktrace):
Reads Queued: 0, 0KiB Writes Queued: 32775, 16387KiB
Read Dispatches: 0, 0KiB Write Dispatches: 32775, 16387KiB
Reads Requeued: 0 Writes Requeued: 0
Reads Completed: 0, 0KiB Writes Completed: 32775, 16387KiB
Read Merges: 0, 0KiB Write Merges: 0, 0KiB
IO unplugs: 2 Timer unplugs: 0
[After] : 1 sec.
Total (blktrace):
Reads Queued: 0, 0KiB Writes Queued: 13, 6KiB
Read Dispatches: 0, 0KiB Write Dispatches: 13, 6KiB
Reads Requeued: 0 Writes Requeued: 0
Reads Completed: 0, 0KiB Writes Completed: 13, 6KiB
Read Merges: 0, 0KiB Write Merges: 0, 0KiB
IO unplugs: 1 Timer unplugs: 0
Signed-off-by: Hyeongseok Kim <hyeongseok@gmail.com>
Acked-by: Sungjong Seo <sj1557.seo@samsung.com>
Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com>
2021-01-31 20:09:48 -05:00
|
|
|
if (err)
|
2020-02-03 07:47:19 -05:00
|
|
|
goto dec_used_clus;
|
|
|
|
} while (clu != EXFAT_EOF_CLUSTER);
|
|
|
|
}
|
|
|
|
|
|
|
|
dec_used_clus:
|
|
|
|
sbi->used_clusters -= num_clusters;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-24 19:09:50 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-02-03 07:47:19 -05:00
|
|
|
int exfat_find_last_cluster(struct super_block *sb, struct exfat_chain *p_chain,
|
|
|
|
unsigned int *ret_clu)
|
|
|
|
{
|
|
|
|
unsigned int clu, next;
|
|
|
|
unsigned int count = 0;
|
|
|
|
|
|
|
|
next = p_chain->dir;
|
|
|
|
if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {
|
|
|
|
*ret_clu = next + p_chain->size - 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
count++;
|
|
|
|
clu = next;
|
|
|
|
if (exfat_ent_get(sb, clu, &next))
|
|
|
|
return -EIO;
|
|
|
|
} while (next != EXFAT_EOF_CLUSTER);
|
|
|
|
|
|
|
|
if (p_chain->size != count) {
|
|
|
|
exfat_fs_error(sb,
|
|
|
|
"bogus directory size (clus : ondisk(%d) != counted(%d))",
|
|
|
|
p_chain->size, count);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ret_clu = clu;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-04-08 09:14:06 -04:00
|
|
|
int exfat_zeroed_cluster(struct inode *dir, unsigned int clu)
|
|
|
|
{
|
|
|
|
struct super_block *sb = dir->i_sb;
|
|
|
|
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
|
|
|
struct buffer_head *bh;
|
2022-08-23 19:20:34 -04:00
|
|
|
sector_t blknr, last_blknr, i;
|
2022-04-08 09:14:06 -04:00
|
|
|
|
|
|
|
blknr = exfat_cluster_to_sector(sbi, clu);
|
|
|
|
last_blknr = blknr + sbi->sect_per_clus;
|
|
|
|
|
|
|
|
if (last_blknr > sbi->num_sectors && sbi->num_sectors > 0) {
|
|
|
|
exfat_fs_error_ratelimit(sb,
|
|
|
|
"%s: out of range(sect:%llu len:%u)",
|
|
|
|
__func__, (unsigned long long)blknr,
|
|
|
|
sbi->sect_per_clus);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Zeroing the unused blocks on this cluster */
|
|
|
|
for (i = blknr; i < last_blknr; i++) {
|
|
|
|
bh = sb_getblk(sb, i);
|
|
|
|
if (!bh)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
memset(bh->b_data, 0, sb->s_blocksize);
|
|
|
|
set_buffer_uptodate(bh);
|
|
|
|
mark_buffer_dirty(bh);
|
|
|
|
brelse(bh);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_DIRSYNC(dir))
|
2022-05-19 09:40:35 -04:00
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)
|
2022-04-08 09:14:06 -04:00
|
|
|
return sync_blockdev_range(sb->s_bdev,
|
|
|
|
EXFAT_BLK_TO_B(blknr, sb),
|
|
|
|
EXFAT_BLK_TO_B(last_blknr, sb) - 1);
|
|
|
|
#else
|
2022-05-19 09:40:35 -04:00
|
|
|
return filemap_write_and_wait_range(sb->s_bdev->bd_inode->i_mapping,
|
|
|
|
EXFAT_BLK_TO_B(blknr, sb),
|
|
|
|
EXFAT_BLK_TO_B(last_blknr, sb) - 1);
|
|
|
|
#endif
|
2020-02-03 07:47:19 -05:00
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
2021-03-18 02:23:56 -04:00
|
|
|
struct exfat_chain *p_chain, bool sync_bmap)
|
2020-02-03 07:47:19 -05:00
|
|
|
{
|
|
|
|
int ret = -ENOSPC;
|
|
|
|
unsigned int num_clusters = 0, total_cnt;
|
|
|
|
unsigned int hint_clu, new_clu, last_clu = EXFAT_EOF_CLUSTER;
|
|
|
|
struct super_block *sb = inode->i_sb;
|
|
|
|
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
|
|
|
|
|
|
|
total_cnt = EXFAT_DATA_CLUSTER_COUNT(sbi);
|
|
|
|
|
|
|
|
if (unlikely(total_cnt < sbi->used_clusters)) {
|
|
|
|
exfat_fs_error_ratelimit(sb,
|
|
|
|
"%s: invalid used clusters(t:%u,u:%u)\n",
|
|
|
|
__func__, total_cnt, sbi->used_clusters);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_alloc > total_cnt - sbi->used_clusters)
|
|
|
|
return -ENOSPC;
|
|
|
|
|
2021-03-24 19:09:50 -04:00
|
|
|
mutex_lock(&sbi->bitmap_lock);
|
|
|
|
|
2020-02-03 07:47:19 -05:00
|
|
|
hint_clu = p_chain->dir;
|
|
|
|
/* find new cluster */
|
|
|
|
if (hint_clu == EXFAT_EOF_CLUSTER) {
|
|
|
|
if (sbi->clu_srch_ptr < EXFAT_FIRST_CLUSTER) {
|
2022-07-28 21:21:39 -04:00
|
|
|
exfat_err(sb, "sbi->clu_srch_ptr is invalid (%u)",
|
2020-04-05 23:49:36 -04:00
|
|
|
sbi->clu_srch_ptr);
|
2020-02-03 07:47:19 -05:00
|
|
|
sbi->clu_srch_ptr = EXFAT_FIRST_CLUSTER;
|
|
|
|
}
|
|
|
|
|
|
|
|
hint_clu = exfat_find_free_bitmap(sb, sbi->clu_srch_ptr);
|
2021-03-24 19:09:50 -04:00
|
|
|
if (hint_clu == EXFAT_EOF_CLUSTER) {
|
|
|
|
ret = -ENOSPC;
|
|
|
|
goto unlock;
|
|
|
|
}
|
2020-02-03 07:47:19 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* check cluster validation */
|
2020-06-04 02:45:12 -04:00
|
|
|
if (!is_valid_cluster(sbi, hint_clu)) {
|
2023-02-28 01:30:06 -05:00
|
|
|
if (hint_clu != sbi->num_clusters)
|
|
|
|
exfat_err(sb, "hint_cluster is invalid (%u), rewind to the first cluster",
|
|
|
|
hint_clu);
|
2020-02-03 07:47:19 -05:00
|
|
|
hint_clu = EXFAT_FIRST_CLUSTER;
|
2023-02-23 09:08:08 -05:00
|
|
|
p_chain->flags = ALLOC_FAT_CHAIN;
|
2020-02-03 07:47:19 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
p_chain->dir = EXFAT_EOF_CLUSTER;
|
|
|
|
|
|
|
|
while ((new_clu = exfat_find_free_bitmap(sb, hint_clu)) !=
|
|
|
|
EXFAT_EOF_CLUSTER) {
|
|
|
|
if (new_clu != hint_clu &&
|
|
|
|
p_chain->flags == ALLOC_NO_FAT_CHAIN) {
|
|
|
|
if (exfat_chain_cont_cluster(sb, p_chain->dir,
|
|
|
|
num_clusters)) {
|
|
|
|
ret = -EIO;
|
|
|
|
goto free_cluster;
|
|
|
|
}
|
|
|
|
p_chain->flags = ALLOC_FAT_CHAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update allocation bitmap */
|
2021-03-18 02:23:56 -04:00
|
|
|
if (exfat_set_bitmap(inode, new_clu, sync_bmap)) {
|
2020-02-03 07:47:19 -05:00
|
|
|
ret = -EIO;
|
|
|
|
goto free_cluster;
|
|
|
|
}
|
|
|
|
|
|
|
|
num_clusters++;
|
|
|
|
|
|
|
|
/* update FAT table */
|
|
|
|
if (p_chain->flags == ALLOC_FAT_CHAIN) {
|
|
|
|
if (exfat_ent_set(sb, new_clu, EXFAT_EOF_CLUSTER)) {
|
|
|
|
ret = -EIO;
|
|
|
|
goto free_cluster;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p_chain->dir == EXFAT_EOF_CLUSTER) {
|
|
|
|
p_chain->dir = new_clu;
|
|
|
|
} else if (p_chain->flags == ALLOC_FAT_CHAIN) {
|
|
|
|
if (exfat_ent_set(sb, last_clu, new_clu)) {
|
|
|
|
ret = -EIO;
|
|
|
|
goto free_cluster;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
last_clu = new_clu;
|
|
|
|
|
|
|
|
if (--num_alloc == 0) {
|
|
|
|
sbi->clu_srch_ptr = hint_clu;
|
|
|
|
sbi->used_clusters += num_clusters;
|
|
|
|
|
|
|
|
p_chain->size += num_clusters;
|
2021-03-24 19:09:50 -04:00
|
|
|
mutex_unlock(&sbi->bitmap_lock);
|
2020-02-03 07:47:19 -05:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
hint_clu = new_clu + 1;
|
|
|
|
if (hint_clu >= sbi->num_clusters) {
|
|
|
|
hint_clu = EXFAT_FIRST_CLUSTER;
|
|
|
|
|
|
|
|
if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {
|
|
|
|
if (exfat_chain_cont_cluster(sb, p_chain->dir,
|
|
|
|
num_clusters)) {
|
|
|
|
ret = -EIO;
|
|
|
|
goto free_cluster;
|
|
|
|
}
|
|
|
|
p_chain->flags = ALLOC_FAT_CHAIN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free_cluster:
|
|
|
|
if (num_clusters)
|
2021-03-24 19:09:50 -04:00
|
|
|
__exfat_free_cluster(inode, p_chain);
|
|
|
|
unlock:
|
|
|
|
mutex_unlock(&sbi->bitmap_lock);
|
2020-02-03 07:47:19 -05:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int exfat_count_num_clusters(struct super_block *sb,
|
|
|
|
struct exfat_chain *p_chain, unsigned int *ret_count)
|
|
|
|
{
|
|
|
|
unsigned int i, count;
|
|
|
|
unsigned int clu;
|
|
|
|
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
|
|
|
|
|
|
|
if (!p_chain->dir || p_chain->dir == EXFAT_EOF_CLUSTER) {
|
|
|
|
*ret_count = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {
|
|
|
|
*ret_count = p_chain->size;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
clu = p_chain->dir;
|
|
|
|
count = 0;
|
|
|
|
for (i = EXFAT_FIRST_CLUSTER; i < sbi->num_clusters; i++) {
|
|
|
|
count++;
|
|
|
|
if (exfat_ent_get(sb, clu, &clu))
|
|
|
|
return -EIO;
|
|
|
|
if (clu == EXFAT_EOF_CLUSTER)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ret_count = count;
|
|
|
|
return 0;
|
|
|
|
}
|