Commit a62fe210 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull exfat updates from Namjae Jeon:

 - Implement FALLOC_FL_ALLOCATE_RANGE to add support for preallocating
   clusters without zeroing, helping to reduce file fragmentation

 - Add a unified block readahead helper for FAT chain conversion, bitmap
   allocation, and directory entry lookups

 - Optimize exfat_chain_cont_cluster() by caching buffer heads to
   minimize mark_buffer_dirty() and mirroring overhead during
   NO_FAT_CHAIN to FAT_CHAIN conversion

 - Switch to truncate_inode_pages_final() in evict_inode() to prevent
   BUG_ON caused by shadow entries during reclaim

 - Fix a 32-bit truncation bug in directory entry calculations by
   ensuring proper bitwise coercion

 - Fix sb->s_maxbytes calculation to correctly reflect the maximum
   possible volume size for a given cluster size, resolving xfstests
   generic/213

 - Introduced exfat_cluster_walk() helper to traverse FAT chains by a
   specified step, handling both ALLOC_NO_FAT_CHAIN and ALLOC_FAT_CHAIN
   modes

 - Introduced exfat_chain_advance() helper to advance an exfat_chain
   structure, updating both the current cluster and remaining size

 - Remove dead assignments and fix Smatch warnings

* tag 'exfat-for-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat:
  exfat: use exfat_chain_advance helper
  exfat: introduce exfat_chain_advance helper
  exfat: remove NULL cache pointer case in exfat_ent_get
  exfat: use exfat_cluster_walk helper
  exfat: introduce exfat_cluster_walk helper
  exfat: fix incorrect directory checksum after rename to shorter name
  exfat: fix s_maxbytes
  exfat: fix passing zero to ERR_PTR() in exfat_mkdir()
  exfat: fix error handling for FAT table operations
  exfat: optimize exfat_chain_cont_cluster with cached buffer heads
  exfat: drop redundant sec parameter from exfat_mirror_bh
  exfat: use readahead helper in exfat_get_dentry
  exfat: use readahead helper in exfat_allocate_bitmap
  exfat: add block readahead in exfat_chain_cont_cluster
  exfat: add fallocate FALLOC_FL_ALLOCATE_RANGE support
  exfat: Fix bitwise operation having different size
  exfat: Drop dead assignment of num_clusters
  exfat: use truncate_inode_pages_final() at evict_inode()
parents f2729827 08cf4a81
Loading
Loading
Loading
Loading
+6 −12
Original line number Diff line number Diff line
@@ -74,11 +74,10 @@ static int exfat_allocate_bitmap(struct super_block *sb,
		struct exfat_dentry *ep)
{
	struct exfat_sb_info *sbi = EXFAT_SB(sb);
	struct blk_plug plug;
	long long map_size;
	unsigned int i, j, need_map_size;
	sector_t sector;
	unsigned int max_ra_count;
	sector_t sector, end, ra;
	blkcnt_t ra_cnt = 0;

	sbi->map_clu = le32_to_cpu(ep->dentry.bitmap.start_clu);
	map_size = le64_to_cpu(ep->dentry.bitmap.size);
@@ -100,17 +99,12 @@ static int exfat_allocate_bitmap(struct super_block *sb,
	if (!sbi->vol_amap)
		return -ENOMEM;

	sector = exfat_cluster_to_sector(sbi, sbi->map_clu);
	max_ra_count = min(sb->s_bdi->ra_pages, sb->s_bdi->io_pages) <<
		(PAGE_SHIFT - sb->s_blocksize_bits);
	sector = ra = exfat_cluster_to_sector(sbi, sbi->map_clu);
	end = sector + sbi->map_sectors - 1;

	for (i = 0; i < sbi->map_sectors; i++) {
		/* Trigger the next readahead in advance. */
		if (max_ra_count && 0 == (i % max_ra_count)) {
			blk_start_plug(&plug);
			for (j = i; j < min(max_ra_count, sbi->map_sectors - i) + i; j++)
				sb_breadahead(sb, sector + j);
			blk_finish_plug(&plug);
		}
		exfat_blk_readahead(sb, sector + i, &ra, &ra_cnt, end);

		sbi->vol_amap[i] = sb_bread(sb, sector + i);
		if (!sbi->vol_amap[i])
+45 −128
Original line number Diff line number Diff line
@@ -93,25 +93,19 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
	clu_offset = EXFAT_DEN_TO_CLU(dentry, sbi);
	exfat_chain_dup(&clu, &dir);

	if (clu.flags == ALLOC_NO_FAT_CHAIN) {
		clu.dir += clu_offset;
		clu.size -= clu_offset;
	} else {
	if (clu.flags == ALLOC_FAT_CHAIN) {
		/* hint_information */
		if (clu_offset > 0 && ei->hint_bmap.off != EXFAT_EOF_CLUSTER &&
		    ei->hint_bmap.off > 0 && clu_offset >= ei->hint_bmap.off) {
			clu_offset -= ei->hint_bmap.off;
			clu.dir = ei->hint_bmap.clu;
			clu.size -= ei->hint_bmap.off;
		}
	}

		while (clu_offset > 0 && clu.dir != EXFAT_EOF_CLUSTER) {
			if (exfat_get_next_cluster(sb, &(clu.dir)))
	if (exfat_chain_advance(sb, &clu, clu_offset))
		return -EIO;

			clu_offset--;
		}
	}

	while (clu.dir != EXFAT_EOF_CLUSTER && dentry < max_dentries) {
		i = dentry & (dentries_per_clu - 1);

@@ -160,16 +154,9 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
			return 0;
		}

		if (clu.flags == ALLOC_NO_FAT_CHAIN) {
			if (--clu.size > 0)
				clu.dir++;
			else
				clu.dir = EXFAT_EOF_CLUSTER;
		} else {
			if (exfat_get_next_cluster(sb, &(clu.dir)))
		if (exfat_chain_advance(sb, &clu, 1))
			return -EIO;
	}
	}

out:
	dir_entry->namebuf.lfn[0] = '\0';
@@ -249,7 +236,7 @@ static int exfat_iterate(struct file *file, struct dir_context *ctx)
		 */
		if (err == -EIO) {
			cpos += 1 << (sb->s_blocksize_bits);
			cpos &= ~(sb->s_blocksize - 1);
			cpos &= ~(loff_t)(sb->s_blocksize - 1);
		}

		err = -EIO;
@@ -490,6 +477,7 @@ void exfat_init_ext_entry(struct exfat_entry_set_cache *es, int num_entries,
	unsigned short *uniname = p_uniname->name;
	struct exfat_dentry *ep;

	es->num_entries = num_entries;
	ep = exfat_get_dentry_cached(es, ES_IDX_FILE);
	ep->dentry.file.num_ext = (unsigned char)(num_entries - 1);

@@ -561,38 +549,6 @@ int exfat_put_dentry_set(struct exfat_entry_set_cache *es, int sync)
	return err;
}

static int exfat_walk_fat_chain(struct super_block *sb,
		struct exfat_chain *p_dir, unsigned int byte_offset,
		unsigned int *clu)
{
	struct exfat_sb_info *sbi = EXFAT_SB(sb);
	unsigned int clu_offset;
	unsigned int cur_clu;

	clu_offset = EXFAT_B_TO_CLU(byte_offset, sbi);
	cur_clu = p_dir->dir;

	if (p_dir->flags == ALLOC_NO_FAT_CHAIN) {
		cur_clu += clu_offset;
	} else {
		while (clu_offset > 0) {
			if (exfat_get_next_cluster(sb, &cur_clu))
				return -EIO;
			if (cur_clu == EXFAT_EOF_CLUSTER) {
				exfat_fs_error(sb,
					"invalid dentry access beyond EOF (clu : %u, eidx : %d)",
					p_dir->dir,
					EXFAT_B_TO_DEN(byte_offset));
				return -EIO;
			}
			clu_offset--;
		}
	}

	*clu = cur_clu;
	return 0;
}

static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir,
			       int entry, sector_t *sector, int *offset)
{
@@ -602,10 +558,19 @@ static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir

	off = EXFAT_DEN_TO_B(entry);

	ret = exfat_walk_fat_chain(sb, p_dir, off, &clu);
	clu = p_dir->dir;
	ret = exfat_cluster_walk(sb, &clu, EXFAT_B_TO_CLU(off, sbi), p_dir->flags);
	if (ret)
		return ret;

	if (clu == EXFAT_EOF_CLUSTER) {
		exfat_fs_error(sb,
			"unexpected early break in cluster chain (clu : %u, len : %d)",
			p_dir->dir,
			EXFAT_B_TO_CLU(off, sbi));
		return -EIO;
	}

	if (!exfat_test_bitmap(sb, clu)) {
		exfat_err(sb, "failed to test cluster bit(%u)", clu);
		return -EIO;
@@ -623,44 +588,11 @@ static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir
	return 0;
}

#define EXFAT_MAX_RA_SIZE     (128*1024)
static int exfat_dir_readahead(struct super_block *sb, sector_t sec)
{
	struct exfat_sb_info *sbi = EXFAT_SB(sb);
	struct buffer_head *bh;
	unsigned int max_ra_count = EXFAT_MAX_RA_SIZE >> sb->s_blocksize_bits;
	unsigned int page_ra_count = PAGE_SIZE >> sb->s_blocksize_bits;
	unsigned int adj_ra_count = max(sbi->sect_per_clus, page_ra_count);
	unsigned int ra_count = min(adj_ra_count, max_ra_count);

	/* Read-ahead is not required */
	if (sbi->sect_per_clus == 1)
		return 0;

	if (sec < sbi->data_start_sector) {
		exfat_err(sb, "requested sector is invalid(sect:%llu, root:%llu)",
			  (unsigned long long)sec, sbi->data_start_sector);
		return -EIO;
	}

	/* Not sector aligned with ra_count, resize ra_count to page size */
	if ((sec - sbi->data_start_sector) & (ra_count - 1))
		ra_count = page_ra_count;

	bh = sb_find_get_block(sb, sec);
	if (!bh || !buffer_uptodate(bh)) {
		unsigned int i;

		for (i = 0; i < ra_count; i++)
			sb_breadahead(sb, (sector_t)(sec + i));
	}
	brelse(bh);
	return 0;
}

struct exfat_dentry *exfat_get_dentry(struct super_block *sb,
		struct exfat_chain *p_dir, int entry, struct buffer_head **bh)
{
	struct exfat_sb_info *sbi = EXFAT_SB(sb);
	unsigned int sect_per_clus = sbi->sect_per_clus;
	unsigned int dentries_per_page = EXFAT_B_TO_DEN(PAGE_SIZE);
	int off;
	sector_t sec;
@@ -673,9 +605,18 @@ struct exfat_dentry *exfat_get_dentry(struct super_block *sb,
	if (exfat_find_location(sb, p_dir, entry, &sec, &off))
		return NULL;

	if (p_dir->dir != EXFAT_FREE_CLUSTER &&
			!(entry & (dentries_per_page - 1)))
		exfat_dir_readahead(sb, sec);
	if (sect_per_clus > 1 &&
	    (entry & (dentries_per_page - 1)) == 0) {
		sector_t ra = sec;
		blkcnt_t cnt = 0;
		unsigned int ra_count = sect_per_clus;

		/* Not sector aligned with ra_count, resize ra_count to page size */
		if ((sec - sbi->data_start_sector) & (ra_count - 1))
			ra_count = PAGE_SIZE >> sb->s_blocksize_bits;

		exfat_blk_readahead(sb, sec, &ra, &cnt, sec + ra_count - 1);
	}

	*bh = sb_bread(sb, sec);
	if (!*bh)
@@ -815,9 +756,7 @@ static int __exfat_get_dentry_set(struct exfat_entry_set_cache *es,
		if (exfat_is_last_sector_in_cluster(sbi, sec)) {
			unsigned int clu = exfat_sector_to_cluster(sbi, sec);

			if (p_dir->flags == ALLOC_NO_FAT_CHAIN)
				clu++;
			else if (exfat_get_next_cluster(sb, &clu))
			if (exfat_cluster_walk(sb, &clu, 1, p_dir->flags))
				goto put_es;
			sec = exfat_cluster_to_sector(sbi, clu);
		} else {
@@ -1133,20 +1072,13 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
			step = DIRENT_STEP_FILE;
		}

		if (clu.flags == ALLOC_NO_FAT_CHAIN) {
			if (--clu.size > 0)
				clu.dir++;
			else
				clu.dir = EXFAT_EOF_CLUSTER;
		} else {
			if (exfat_get_next_cluster(sb, &clu.dir))
		if (exfat_chain_advance(sb, &clu, 1))
			return -EIO;

		/* break if the cluster chain includes a loop */
		if (unlikely(++clu_count > EXFAT_DATA_CLUSTER_COUNT(sbi)))
			goto not_found;
	}
	}

not_found:
	/*
@@ -1180,14 +1112,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
	if (!((dentry + 1) & (dentries_per_clu - 1))) {
		int ret = 0;

		if (clu.flags == ALLOC_NO_FAT_CHAIN) {
			if (--clu.size > 0)
				clu.dir++;
			else
				clu.dir = EXFAT_EOF_CLUSTER;
		} else {
			ret = exfat_get_next_cluster(sb, &clu.dir);
		}
		ret = exfat_chain_advance(sb, &clu, 1);

		if (ret || clu.dir == EXFAT_EOF_CLUSTER) {
			/* just initialized hint_stat */
@@ -1232,21 +1157,13 @@ int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir)
			count++;
		}

		if (clu.flags == ALLOC_NO_FAT_CHAIN) {
			if (--clu.size > 0)
				clu.dir++;
			else
				clu.dir = EXFAT_EOF_CLUSTER;
		} else {
			if (exfat_get_next_cluster(sb, &(clu.dir)))
		if (exfat_chain_advance(sb, &clu, 1))
			return -EIO;

		if (unlikely(++clu_count > sbi->used_clusters)) {
			exfat_fs_error(sb, "FAT or bitmap is corrupted");
			return -EIO;
		}

		}
	}

	return count;
+53 −4
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#include <linux/ratelimit.h>
#include <linux/nls.h>
#include <linux/blkdev.h>
#include <linux/backing-dev.h>
#include <uapi/linux/exfat.h>

#define EXFAT_ROOT_INO		1
@@ -79,6 +80,10 @@ enum {
#define EXFAT_HINT_NONE		-1
#define EXFAT_MIN_SUBDIR	2

#define EXFAT_BLK_RA_SIZE(sb)		\
	(min_t(blkcnt_t, (sb)->s_bdi->ra_pages, (sb)->s_bdi->io_pages) \
	<< (PAGE_SHIFT - (sb)->s_blocksize_bits))

/*
 * helpers for cluster size to byte conversion.
 */
@@ -117,9 +122,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))

/*
 * helpers for bitmap.
@@ -432,7 +437,8 @@ int exfat_set_volume_dirty(struct super_block *sb);
int exfat_clear_volume_dirty(struct super_block *sb);

/* fatent.c */
#define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu, NULL)
#define exfat_get_next_cluster(sb, pclu) \
	exfat_cluster_walk(sb, (pclu), 1, ALLOC_FAT_CHAIN)

int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
		struct exfat_chain *p_chain, bool sync_bmap);
@@ -448,6 +454,28 @@ int exfat_find_last_cluster(struct super_block *sb, struct exfat_chain *p_chain,
		unsigned int *ret_clu);
int exfat_count_num_clusters(struct super_block *sb,
		struct exfat_chain *p_chain, unsigned int *ret_count);
int exfat_blk_readahead(struct super_block *sb, sector_t sec,
		sector_t *ra, blkcnt_t *ra_cnt, sector_t end);

static inline int
exfat_cluster_walk(struct super_block *sb, unsigned int *clu,
		unsigned int step, int flags)
{
	struct buffer_head *bh = NULL;

	if (flags == ALLOC_NO_FAT_CHAIN) {
		(*clu) += step;
		return 0;
	}

	while (step--) {
		if (exfat_ent_get(sb, *clu, clu, &bh))
			return -EIO;
	}
	brelse(bh);

	return 0;
}

/* balloc.c */
int exfat_load_bitmap(struct super_block *sb);
@@ -524,6 +552,27 @@ int exfat_read_volume_label(struct super_block *sb,
int exfat_write_volume_label(struct super_block *sb,
			     struct exfat_uni_name *label);

static inline int exfat_chain_advance(struct super_block *sb,
		struct exfat_chain *chain, unsigned int step)
{
	unsigned int clu = chain->dir;

	if (unlikely(chain->size < step))
		return -EIO;

	if (exfat_cluster_walk(sb, &clu, step, chain->flags))
		return -EIO;

	chain->size -= step;

	if (chain->size == 0 && chain->flags == ALLOC_NO_FAT_CHAIN)
		chain->dir = EXFAT_EOF_CLUSTER;
	else
		chain->dir = clu;

	return 0;
}

/* inode.c */
extern const struct inode_operations exfat_file_inode_operations;
void exfat_sync_inode(struct inode *inode);
@@ -577,7 +626,7 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
		u8 *tz, __le16 *time, __le16 *date, u8 *time_cs);
u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type);
u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type);
void exfat_update_bh(struct buffer_head *bh, int sync);
int exfat_update_bh(struct buffer_head *bh, int sync);
int exfat_update_bhs(struct buffer_head **bhs, int nr_bhs, int sync);
void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
		unsigned int size, unsigned char flags);
+1 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#define EXFAT_FIRST_CLUSTER	2
#define EXFAT_DATA_CLUSTER_COUNT(sbi)	\
	((sbi)->num_clusters - EXFAT_RESERVED_CLUSTERS)
#define EXFAT_MAX_NUM_CLUSTER		(0xFFFFFFF5)

/* AllocationPossible and NoFatChain field in GeneralSecondaryFlags Field */
#define ALLOC_POSSIBLE		0x01
+85 −31
Original line number Diff line number Diff line
@@ -11,11 +11,11 @@
#include "exfat_raw.h"
#include "exfat_fs.h"

static int exfat_mirror_bh(struct super_block *sb, sector_t sec,
		struct buffer_head *bh)
static int exfat_mirror_bh(struct super_block *sb, struct buffer_head *bh)
{
	struct buffer_head *c_bh;
	struct exfat_sb_info *sbi = EXFAT_SB(sb);
	sector_t sec = bh->b_blocknr;
	sector_t sec2;
	int err = 0;

@@ -25,22 +25,30 @@ static int exfat_mirror_bh(struct super_block *sb, sector_t sec,
		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);
		if (sb->s_flags & SB_SYNCHRONOUS)
			err = sync_dirty_buffer(c_bh);
		err = exfat_update_bh(c_bh, sb->s_flags & SB_SYNCHRONOUS);
		brelse(c_bh);
	}

	return err;
}

static int exfat_end_bh(struct super_block *sb, struct buffer_head *bh)
{
	int err;

	err = exfat_update_bh(bh, sb->s_flags & SB_SYNCHRONOUS);
	if (!err)
		err = exfat_mirror_bh(sb, bh);
	brelse(bh);
	return err;
}

static int __exfat_ent_get(struct super_block *sb, unsigned int loc,
		unsigned int *content, struct buffer_head **last)
		unsigned int *content, struct buffer_head **cache)
{
	unsigned int off;
	sector_t sec;
	struct buffer_head *bh = last ? *last : NULL;
	struct buffer_head *bh = *cache;

	sec = FAT_ENT_OFFSET_SECTOR(sb, loc);
	off = FAT_ENT_OFFSET_BYTE_IN_SECTOR(sb, loc);
@@ -48,8 +56,7 @@ static int __exfat_ent_get(struct super_block *sb, unsigned int loc,
	if (!bh || bh->b_blocknr != sec || !buffer_uptodate(bh)) {
		brelse(bh);
		bh = sb_bread(sb, sec);
		if (last)
			*last = bh;
		*cache = bh;
		if (unlikely(!bh))
			return -EIO;
	}
@@ -60,39 +67,48 @@ static int __exfat_ent_get(struct super_block *sb, unsigned int loc,
	if (*content > EXFAT_BAD_CLUSTER)
		*content = EXFAT_EOF_CLUSTER;

	if (!last)
		brelse(bh);
	return 0;
}

int exfat_ent_set(struct super_block *sb, unsigned int loc,
		unsigned int content)
static int __exfat_ent_set(struct super_block *sb, unsigned int loc,
		unsigned int content, struct buffer_head **cache)
{
	unsigned int off;
	sector_t sec;
	__le32 *fat_entry;
	struct buffer_head *bh;
	struct buffer_head *bh = cache ? *cache : NULL;
	unsigned int off;

	sec = FAT_ENT_OFFSET_SECTOR(sb, loc);
	off = FAT_ENT_OFFSET_BYTE_IN_SECTOR(sb, loc);

	if (!bh || bh->b_blocknr != sec || !buffer_uptodate(bh)) {
		if (bh)
			exfat_end_bh(sb, bh);
		bh = sb_bread(sb, sec);
	if (!bh)
		if (cache)
			*cache = bh;
		if (unlikely(!bh))
			return -EIO;
	}

	fat_entry = (__le32 *)&(bh->b_data[off]);
	*fat_entry = cpu_to_le32(content);
	exfat_update_bh(bh, sb->s_flags & SB_SYNCHRONOUS);
	exfat_mirror_bh(sb, sec, bh);
	brelse(bh);
	if (!cache)
		exfat_end_bh(sb, bh);
	return 0;
}

int exfat_ent_set(struct super_block *sb, unsigned int loc,
		unsigned int content)
{
	return __exfat_ent_set(sb, loc, content, NULL);
}

/*
 * Caller must release the buffer_head if no error return.
 */
int exfat_ent_get(struct super_block *sb, unsigned int loc,
		unsigned int *content, struct buffer_head **last)
		unsigned int *content, struct buffer_head **cache)
{
	struct exfat_sb_info *sbi = EXFAT_SB(sb);

@@ -103,7 +119,7 @@ int exfat_ent_get(struct super_block *sb, unsigned int loc,
		goto err;
	}

	if (unlikely(__exfat_ent_get(sb, loc, content, last))) {
	if (unlikely(__exfat_ent_get(sb, loc, content, cache))) {
		exfat_fs_error_ratelimit(sb,
			"failed to access to FAT (entry 0x%08x)",
			loc);
@@ -132,31 +148,69 @@ int exfat_ent_get(struct super_block *sb, unsigned int loc,
	}

	return 0;
err:
	if (last) {
		brelse(*last);

err:
	/* Avoid double release */
		*last = NULL;
	}
	brelse(*cache);
	*cache = NULL;
	return -EIO;
}

int exfat_blk_readahead(struct super_block *sb, sector_t sec,
		sector_t *ra, blkcnt_t *ra_cnt, sector_t end)
{
	struct blk_plug plug;

	if (sec < *ra)
		return 0;

	*ra += *ra_cnt;

	/* No blocks left (or only the last block), skip readahead. */
	if (*ra >= end)
		return 0;

	*ra_cnt = min(end - *ra + 1, EXFAT_BLK_RA_SIZE(sb));
	if (*ra_cnt == 0) {
		/* Move 'ra' to the end to disable readahead. */
		*ra = end;
		return 0;
	}

	blk_start_plug(&plug);
	for (unsigned int i = 0; i < *ra_cnt; i++)
		sb_breadahead(sb, *ra + i);
	blk_finish_plug(&plug);
	return 0;
}

int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain,
		unsigned int len)
{
	struct buffer_head *bh = NULL;
	sector_t sec, end, ra;
	blkcnt_t ra_cnt = 0;

	if (!len)
		return 0;

	ra = FAT_ENT_OFFSET_SECTOR(sb, chain);
	end = FAT_ENT_OFFSET_SECTOR(sb, chain + len - 1);

	while (len > 1) {
		if (exfat_ent_set(sb, chain, chain + 1))
		sec = FAT_ENT_OFFSET_SECTOR(sb, chain);
		exfat_blk_readahead(sb, sec, &ra, &ra_cnt, end);

		if (__exfat_ent_set(sb, chain, chain + 1, &bh))
			return -EIO;
		chain++;
		len--;
	}

	if (exfat_ent_set(sb, chain, EXFAT_EOF_CLUSTER))
	if (__exfat_ent_set(sb, chain, EXFAT_EOF_CLUSTER, &bh))
		return -EIO;

	exfat_end_bh(sb, bh);
	return 0;
}

Loading