1 /* 2 * Copyright (C) Tino Reichardt, 2012 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 12 * the GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 */ 18 19 #include <linux/fs.h> 20 #include <linux/slab.h> 21 #include <linux/blkdev.h> 22 23 #include "jfs_incore.h" 24 #include "jfs_superblock.h" 25 #include "jfs_discard.h" 26 #include "jfs_dmap.h" 27 #include "jfs_debug.h" 28 29 30 /* 31 * NAME: jfs_issue_discard() 32 * 33 * FUNCTION: TRIM the specified block range on device, if supported 34 * 35 * PARAMETERS: 36 * ip - pointer to in-core inode 37 * blkno - starting block number to be trimmed (0..N) 38 * nblocks - number of blocks to be trimmed 39 * 40 * RETURN VALUES: 41 * none 42 * 43 * serialization: IREAD_LOCK(ipbmap) held on entry/exit; 44 */ 45 void jfs_issue_discard(struct inode *ip, u64 blkno, u64 nblocks) 46 { 47 struct super_block *sb = ip->i_sb; 48 int r = 0; 49 50 r = sb_issue_discard(sb, blkno, nblocks, GFP_NOFS, 0); 51 if (unlikely(r != 0)) { 52 jfs_err("JFS: sb_issue_discard(%p, %llu, %llu, GFP_NOFS, 0) = %d => failed!", 53 sb, (unsigned long long)blkno, 54 (unsigned long long)nblocks, r); 55 } 56 57 jfs_info("JFS: sb_issue_discard(%p, %llu, %llu, GFP_NOFS, 0) = %d", 58 sb, (unsigned long long)blkno, 59 (unsigned long long)nblocks, r); 60 61 return; 62 } 63 64 /* 65 * NAME: jfs_ioc_trim() 66 * 67 * FUNCTION: attempt to discard (TRIM) all free blocks from the 68 * filesystem. 69 * 70 * PARAMETERS: 71 * ip - pointer to in-core inode; 72 * range - the range, given by user space 73 * 74 * RETURN VALUES: 75 * 0 - success 76 * -EIO - i/o error 77 */ 78 int jfs_ioc_trim(struct inode *ip, struct fstrim_range *range) 79 { 80 struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap; 81 struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap; 82 struct super_block *sb = ipbmap->i_sb; 83 int agno, agno_end; 84 u64 start, end, minlen; 85 u64 trimmed = 0; 86 87 /** 88 * convert byte values to block size of filesystem: 89 * start: First Byte to trim 90 * len: number of Bytes to trim from start 91 * minlen: minimum extent length in Bytes 92 */ 93 start = range->start >> sb->s_blocksize_bits; 94 end = start + (range->len >> sb->s_blocksize_bits) - 1; 95 minlen = range->minlen >> sb->s_blocksize_bits; 96 if (minlen == 0) 97 minlen = 1; 98 99 if (minlen > bmp->db_agsize || 100 start >= bmp->db_mapsize || 101 range->len < sb->s_blocksize) 102 return -EINVAL; 103 104 if (end >= bmp->db_mapsize) 105 end = bmp->db_mapsize - 1; 106 107 /** 108 * we trim all ag's within the range 109 */ 110 agno = BLKTOAG(start, JFS_SBI(ip->i_sb)); 111 agno_end = BLKTOAG(end, JFS_SBI(ip->i_sb)); 112 while (agno <= agno_end) { 113 trimmed += dbDiscardAG(ip, agno, minlen); 114 agno++; 115 } 116 range->len = trimmed << sb->s_blocksize_bits; 117 118 return 0; 119 } 120