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" \ 53 "(%p, %llu, %llu, GFP_NOFS, 0) = %d => failed!\n", 54 sb, (unsigned long long)blkno, 55 (unsigned long long)nblocks, r); 56 } 57 58 jfs_info("JFS: sb_issue_discard" \ 59 "(%p, %llu, %llu, GFP_NOFS, 0) = %d\n", 60 sb, (unsigned long long)blkno, 61 (unsigned long long)nblocks, r); 62 63 return; 64 } 65 66 /* 67 * NAME: jfs_ioc_trim() 68 * 69 * FUNCTION: attempt to discard (TRIM) all free blocks from the 70 * filesystem. 71 * 72 * PARAMETERS: 73 * ip - pointer to in-core inode; 74 * range - the range, given by user space 75 * 76 * RETURN VALUES: 77 * 0 - success 78 * -EIO - i/o error 79 */ 80 int jfs_ioc_trim(struct inode *ip, struct fstrim_range *range) 81 { 82 struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap; 83 struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap; 84 struct super_block *sb = ipbmap->i_sb; 85 int agno, agno_end; 86 u64 start, end, minlen; 87 u64 trimmed = 0; 88 89 /** 90 * convert byte values to block size of filesystem: 91 * start: First Byte to trim 92 * len: number of Bytes to trim from start 93 * minlen: minimum extent length in Bytes 94 */ 95 start = range->start >> sb->s_blocksize_bits; 96 end = start + (range->len >> sb->s_blocksize_bits) - 1; 97 minlen = range->minlen >> sb->s_blocksize_bits; 98 if (minlen == 0) 99 minlen = 1; 100 101 if (minlen > bmp->db_agsize || 102 start >= bmp->db_mapsize || 103 range->len < sb->s_blocksize) 104 return -EINVAL; 105 106 if (end >= bmp->db_mapsize) 107 end = bmp->db_mapsize - 1; 108 109 /** 110 * we trim all ag's within the range 111 */ 112 agno = BLKTOAG(start, JFS_SBI(ip->i_sb)); 113 agno_end = BLKTOAG(end, JFS_SBI(ip->i_sb)); 114 while (agno <= agno_end) { 115 trimmed += dbDiscardAG(ip, agno, minlen); 116 agno++; 117 } 118 range->len = trimmed << sb->s_blocksize_bits; 119 120 return 0; 121 } 122