| // SPDX-License-Identifier: GPL-2.0-or-later | 
 | /* | 
 |  *   Copyright (C) Tino Reichardt, 2012 | 
 |  */ | 
 |  | 
 | #include <linux/fs.h> | 
 | #include <linux/slab.h> | 
 | #include <linux/blkdev.h> | 
 |  | 
 | #include "jfs_incore.h" | 
 | #include "jfs_superblock.h" | 
 | #include "jfs_discard.h" | 
 | #include "jfs_dmap.h" | 
 | #include "jfs_debug.h" | 
 |  | 
 |  | 
 | /* | 
 |  * NAME:	jfs_issue_discard() | 
 |  * | 
 |  * FUNCTION:	TRIM the specified block range on device, if supported | 
 |  * | 
 |  * PARAMETERS: | 
 |  *	ip	- pointer to in-core inode | 
 |  *	blkno	- starting block number to be trimmed (0..N) | 
 |  *	nblocks	- number of blocks to be trimmed | 
 |  * | 
 |  * RETURN VALUES: | 
 |  *	none | 
 |  * | 
 |  * serialization: IREAD_LOCK(ipbmap) held on entry/exit; | 
 |  */ | 
 | void jfs_issue_discard(struct inode *ip, u64 blkno, u64 nblocks) | 
 | { | 
 | 	struct super_block *sb = ip->i_sb; | 
 | 	int r = 0; | 
 |  | 
 | 	r = sb_issue_discard(sb, blkno, nblocks, GFP_NOFS, 0); | 
 | 	if (unlikely(r != 0)) { | 
 | 		jfs_err("JFS: sb_issue_discard(%p, %llu, %llu, GFP_NOFS, 0) = %d => failed!", | 
 | 			sb, (unsigned long long)blkno, | 
 | 			(unsigned long long)nblocks, r); | 
 | 	} | 
 |  | 
 | 	jfs_info("JFS: sb_issue_discard(%p, %llu, %llu, GFP_NOFS, 0) = %d", | 
 | 		sb, (unsigned long long)blkno, | 
 | 		(unsigned long long)nblocks, r); | 
 |  | 
 | 	return; | 
 | } | 
 |  | 
 | /* | 
 |  * NAME:	jfs_ioc_trim() | 
 |  * | 
 |  * FUNCTION:	attempt to discard (TRIM) all free blocks from the | 
 |  *              filesystem. | 
 |  * | 
 |  * PARAMETERS: | 
 |  *	ip	- pointer to in-core inode; | 
 |  *	range	- the range, given by user space | 
 |  * | 
 |  * RETURN VALUES: | 
 |  *	0	- success | 
 |  *	-EIO	- i/o error | 
 |  */ | 
 | int jfs_ioc_trim(struct inode *ip, struct fstrim_range *range) | 
 | { | 
 | 	struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap; | 
 | 	struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap; | 
 | 	struct super_block *sb = ipbmap->i_sb; | 
 | 	int agno, agno_end; | 
 | 	u64 start, end, minlen; | 
 | 	u64 trimmed = 0; | 
 |  | 
 | 	/** | 
 | 	 * convert byte values to block size of filesystem: | 
 | 	 * start:	First Byte to trim | 
 | 	 * len:		number of Bytes to trim from start | 
 | 	 * minlen:	minimum extent length in Bytes | 
 | 	 */ | 
 | 	start = range->start >> sb->s_blocksize_bits; | 
 | 	end = start + (range->len >> sb->s_blocksize_bits) - 1; | 
 | 	minlen = range->minlen >> sb->s_blocksize_bits; | 
 | 	if (minlen == 0) | 
 | 		minlen = 1; | 
 |  | 
 | 	if (minlen > bmp->db_agsize || | 
 | 	    start >= bmp->db_mapsize || | 
 | 	    range->len < sb->s_blocksize) | 
 | 		return -EINVAL; | 
 |  | 
 | 	if (end >= bmp->db_mapsize) | 
 | 		end = bmp->db_mapsize - 1; | 
 |  | 
 | 	/** | 
 | 	 * we trim all ag's within the range | 
 | 	 */ | 
 | 	agno = BLKTOAG(start, JFS_SBI(ip->i_sb)); | 
 | 	agno_end = BLKTOAG(end, JFS_SBI(ip->i_sb)); | 
 | 	while (agno <= agno_end) { | 
 | 		trimmed += dbDiscardAG(ip, agno, minlen); | 
 | 		agno++; | 
 | 	} | 
 | 	range->len = trimmed << sb->s_blocksize_bits; | 
 |  | 
 | 	return 0; | 
 | } |