1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) Tino Reichardt, 2012 4 */ 5 6 #include <linux/fs.h> 7 #include <linux/slab.h> 8 #include <linux/blkdev.h> 9 10 #include "jfs_incore.h" 11 #include "jfs_superblock.h" 12 #include "jfs_discard.h" 13 #include "jfs_dmap.h" 14 #include "jfs_debug.h" 15 16 17 /* 18 * NAME: jfs_issue_discard() 19 * 20 * FUNCTION: TRIM the specified block range on device, if supported 21 * 22 * PARAMETERS: 23 * ip - pointer to in-core inode 24 * blkno - starting block number to be trimmed (0..N) 25 * nblocks - number of blocks to be trimmed 26 * 27 * RETURN VALUES: 28 * none 29 * 30 * serialization: IREAD_LOCK(ipbmap) held on entry/exit; 31 */ 32 void jfs_issue_discard(struct inode *ip, u64 blkno, u64 nblocks) 33 { 34 struct super_block *sb = ip->i_sb; 35 int r = 0; 36 37 r = sb_issue_discard(sb, blkno, nblocks, GFP_NOFS, 0); 38 if (unlikely(r != 0)) { 39 jfs_err("JFS: sb_issue_discard(%p, %llu, %llu, GFP_NOFS, 0) = %d => failed!", 40 sb, (unsigned long long)blkno, 41 (unsigned long long)nblocks, r); 42 } 43 44 jfs_info("JFS: sb_issue_discard(%p, %llu, %llu, GFP_NOFS, 0) = %d", 45 sb, (unsigned long long)blkno, 46 (unsigned long long)nblocks, r); 47 48 return; 49 } 50 51 /* 52 * NAME: jfs_ioc_trim() 53 * 54 * FUNCTION: attempt to discard (TRIM) all free blocks from the 55 * filesystem. 56 * 57 * PARAMETERS: 58 * ip - pointer to in-core inode; 59 * range - the range, given by user space 60 * 61 * RETURN VALUES: 62 * 0 - success 63 * -EIO - i/o error 64 */ 65 int jfs_ioc_trim(struct inode *ip, struct fstrim_range *range) 66 { 67 struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap; 68 struct bmap *bmp; 69 struct super_block *sb = ipbmap->i_sb; 70 int agno, agno_end; 71 u64 start, end, minlen; 72 u64 trimmed = 0; 73 74 /** 75 * convert byte values to block size of filesystem: 76 * start: First Byte to trim 77 * len: number of Bytes to trim from start 78 * minlen: minimum extent length in Bytes 79 */ 80 start = range->start >> sb->s_blocksize_bits; 81 end = start + (range->len >> sb->s_blocksize_bits) - 1; 82 minlen = range->minlen >> sb->s_blocksize_bits; 83 if (minlen == 0) 84 minlen = 1; 85 86 down_read(&sb->s_umount); 87 bmp = JFS_SBI(ip->i_sb)->bmap; 88 89 if (minlen > bmp->db_agsize || 90 start >= bmp->db_mapsize || 91 range->len < sb->s_blocksize) { 92 up_read(&sb->s_umount); 93 return -EINVAL; 94 } 95 96 if (end >= bmp->db_mapsize) 97 end = bmp->db_mapsize - 1; 98 99 /** 100 * we trim all ag's within the range 101 */ 102 agno = BLKTOAG(start, JFS_SBI(ip->i_sb)); 103 agno_end = BLKTOAG(end, JFS_SBI(ip->i_sb)); 104 while (agno <= agno_end) { 105 trimmed += dbDiscardAG(ip, agno, minlen); 106 agno++; 107 } 108 109 up_read(&sb->s_umount); 110 range->len = trimmed << sb->s_blocksize_bits; 111 112 return 0; 113 } 114