diff options
| -rw-r--r-- | Documentation/filesystems/jfs.txt | 19 | ||||
| -rw-r--r-- | fs/jfs/Makefile | 2 | ||||
| -rw-r--r-- | fs/jfs/ioctl.c | 43 | ||||
| -rw-r--r-- | fs/jfs/jfs_discard.c | 117 | ||||
| -rw-r--r-- | fs/jfs/jfs_discard.h | 26 | ||||
| -rw-r--r-- | fs/jfs/jfs_dmap.c | 125 | ||||
| -rw-r--r-- | fs/jfs/jfs_dmap.h | 2 | ||||
| -rw-r--r-- | fs/jfs/jfs_filsys.h | 3 | ||||
| -rw-r--r-- | fs/jfs/jfs_incore.h | 1 | ||||
| -rw-r--r-- | fs/jfs/super.c | 71 |
10 files changed, 385 insertions, 24 deletions
diff --git a/Documentation/filesystems/jfs.txt b/Documentation/filesystems/jfs.txt index 26ebde77e821..2f94f9ca1794 100644 --- a/Documentation/filesystems/jfs.txt +++ b/Documentation/filesystems/jfs.txt | |||
| @@ -3,6 +3,7 @@ IBM's Journaled File System (JFS) for Linux | |||
| 3 | JFS Homepage: http://jfs.sourceforge.net/ | 3 | JFS Homepage: http://jfs.sourceforge.net/ |
| 4 | 4 | ||
| 5 | The following mount options are supported: | 5 | The following mount options are supported: |
| 6 | (*) == default | ||
| 6 | 7 | ||
| 7 | iocharset=name Character set to use for converting from Unicode to | 8 | iocharset=name Character set to use for converting from Unicode to |
| 8 | ASCII. The default is to do no conversion. Use | 9 | ASCII. The default is to do no conversion. Use |
| @@ -21,12 +22,12 @@ nointegrity Do not write to the journal. The primary use of this option | |||
| 21 | from backup media. The integrity of the volume is not | 22 | from backup media. The integrity of the volume is not |
| 22 | guaranteed if the system abnormally abends. | 23 | guaranteed if the system abnormally abends. |
| 23 | 24 | ||
| 24 | integrity Default. Commit metadata changes to the journal. Use this | 25 | integrity(*) Commit metadata changes to the journal. Use this option to |
| 25 | option to remount a volume where the nointegrity option was | 26 | remount a volume where the nointegrity option was |
| 26 | previously specified in order to restore normal behavior. | 27 | previously specified in order to restore normal behavior. |
| 27 | 28 | ||
| 28 | errors=continue Keep going on a filesystem error. | 29 | errors=continue Keep going on a filesystem error. |
| 29 | errors=remount-ro Default. Remount the filesystem read-only on an error. | 30 | errors=remount-ro(*) Remount the filesystem read-only on an error. |
| 30 | errors=panic Panic and halt the machine if an error occurs. | 31 | errors=panic Panic and halt the machine if an error occurs. |
| 31 | 32 | ||
| 32 | uid=value Override on-disk uid with specified value | 33 | uid=value Override on-disk uid with specified value |
| @@ -35,6 +36,18 @@ umask=value Override on-disk umask with specified octal value. For | |||
| 35 | directories, the execute bit will be set if the corresponding | 36 | directories, the execute bit will be set if the corresponding |
| 36 | read bit is set. | 37 | read bit is set. |
| 37 | 38 | ||
| 39 | discard=minlen This enables/disables the use of discard/TRIM commands. | ||
| 40 | discard The discard/TRIM commands are sent to the underlying | ||
| 41 | nodiscard(*) block device when blocks are freed. This is useful for SSD | ||
| 42 | devices and sparse/thinly-provisioned LUNs. The FITRIM ioctl | ||
| 43 | command is also available together with the nodiscard option. | ||
| 44 | The value of minlen specifies the minimum blockcount, when | ||
| 45 | a TRIM command to the block device is considered usefull. | ||
| 46 | When no value is given to the discard option, it defaults to | ||
| 47 | 64 blocks, which means 256KiB in JFS. | ||
| 48 | The minlen value of discard overrides the minlen value given | ||
| 49 | on an FITRIM ioctl(). | ||
| 50 | |||
| 38 | Please send bugs, comments, cards and letters to shaggy@linux.vnet.ibm.com. | 51 | Please send bugs, comments, cards and letters to shaggy@linux.vnet.ibm.com. |
| 39 | 52 | ||
| 40 | The JFS mailing list can be subscribed to by using the link labeled | 53 | The JFS mailing list can be subscribed to by using the link labeled |
diff --git a/fs/jfs/Makefile b/fs/jfs/Makefile index a58fa72d7e59..d20d4737b3ef 100644 --- a/fs/jfs/Makefile +++ b/fs/jfs/Makefile | |||
| @@ -6,7 +6,7 @@ obj-$(CONFIG_JFS_FS) += jfs.o | |||
| 6 | 6 | ||
| 7 | jfs-y := super.o file.o inode.o namei.o jfs_mount.o jfs_umount.o \ | 7 | jfs-y := super.o file.o inode.o namei.o jfs_mount.o jfs_umount.o \ |
| 8 | jfs_xtree.o jfs_imap.o jfs_debug.o jfs_dmap.o \ | 8 | jfs_xtree.o jfs_imap.o jfs_debug.o jfs_dmap.o \ |
| 9 | jfs_unicode.o jfs_dtree.o jfs_inode.o \ | 9 | jfs_unicode.o jfs_dtree.o jfs_inode.o jfs_discard.o \ |
| 10 | jfs_extent.o symlink.o jfs_metapage.o \ | 10 | jfs_extent.o symlink.o jfs_metapage.o \ |
| 11 | jfs_logmgr.o jfs_txnmgr.o jfs_uniupr.o \ | 11 | jfs_logmgr.o jfs_txnmgr.o jfs_uniupr.o \ |
| 12 | resize.o xattr.o ioctl.o | 12 | resize.o xattr.o ioctl.o |
diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c index f19d1e04a374..bc555ff417e9 100644 --- a/fs/jfs/ioctl.c +++ b/fs/jfs/ioctl.c | |||
| @@ -11,13 +11,17 @@ | |||
| 11 | #include <linux/mount.h> | 11 | #include <linux/mount.h> |
| 12 | #include <linux/time.h> | 12 | #include <linux/time.h> |
| 13 | #include <linux/sched.h> | 13 | #include <linux/sched.h> |
| 14 | #include <linux/blkdev.h> | ||
| 14 | #include <asm/current.h> | 15 | #include <asm/current.h> |
| 15 | #include <asm/uaccess.h> | 16 | #include <asm/uaccess.h> |
| 16 | 17 | ||
| 18 | #include "jfs_filsys.h" | ||
| 19 | #include "jfs_debug.h" | ||
| 17 | #include "jfs_incore.h" | 20 | #include "jfs_incore.h" |
| 18 | #include "jfs_dinode.h" | 21 | #include "jfs_dinode.h" |
| 19 | #include "jfs_inode.h" | 22 | #include "jfs_inode.h" |
| 20 | 23 | #include "jfs_dmap.h" | |
| 24 | #include "jfs_discard.h" | ||
| 21 | 25 | ||
| 22 | static struct { | 26 | static struct { |
| 23 | long jfs_flag; | 27 | long jfs_flag; |
| @@ -123,6 +127,40 @@ setflags_out: | |||
| 123 | mnt_drop_write_file(filp); | 127 | mnt_drop_write_file(filp); |
| 124 | return err; | 128 | return err; |
| 125 | } | 129 | } |
| 130 | |||
| 131 | case FITRIM: | ||
| 132 | { | ||
| 133 | struct super_block *sb = inode->i_sb; | ||
| 134 | struct request_queue *q = bdev_get_queue(sb->s_bdev); | ||
| 135 | struct fstrim_range range; | ||
| 136 | s64 ret = 0; | ||
| 137 | |||
| 138 | if (!capable(CAP_SYS_ADMIN)) | ||
| 139 | return -EPERM; | ||
| 140 | |||
| 141 | if (!blk_queue_discard(q)) { | ||
| 142 | jfs_warn("FITRIM not supported on device"); | ||
| 143 | return -EOPNOTSUPP; | ||
| 144 | } | ||
| 145 | |||
| 146 | if (copy_from_user(&range, (struct fstrim_range __user *)arg, | ||
| 147 | sizeof(range))) | ||
| 148 | return -EFAULT; | ||
| 149 | |||
| 150 | range.minlen = max_t(unsigned int, range.minlen, | ||
| 151 | q->limits.discard_granularity); | ||
| 152 | |||
| 153 | ret = jfs_ioc_trim(inode, &range); | ||
| 154 | if (ret < 0) | ||
| 155 | return ret; | ||
| 156 | |||
| 157 | if (copy_to_user((struct fstrim_range __user *)arg, &range, | ||
| 158 | sizeof(range))) | ||
| 159 | return -EFAULT; | ||
| 160 | |||
| 161 | return 0; | ||
| 162 | } | ||
| 163 | |||
| 126 | default: | 164 | default: |
| 127 | return -ENOTTY; | 165 | return -ENOTTY; |
| 128 | } | 166 | } |
| @@ -142,6 +180,9 @@ long jfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
| 142 | case JFS_IOC_SETFLAGS32: | 180 | case JFS_IOC_SETFLAGS32: |
| 143 | cmd = JFS_IOC_SETFLAGS; | 181 | cmd = JFS_IOC_SETFLAGS; |
| 144 | break; | 182 | break; |
| 183 | case FITRIM: | ||
| 184 | cmd = FITRIM; | ||
| 185 | break; | ||
| 145 | } | 186 | } |
| 146 | return jfs_ioctl(filp, cmd, arg); | 187 | return jfs_ioctl(filp, cmd, arg); |
| 147 | } | 188 | } |
diff --git a/fs/jfs/jfs_discard.c b/fs/jfs/jfs_discard.c new file mode 100644 index 000000000000..9947563e4175 --- /dev/null +++ b/fs/jfs/jfs_discard.c | |||
| @@ -0,0 +1,117 @@ | |||
| 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 | s64 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 | if (start < 0) | ||
| 97 | start = 0; | ||
| 98 | end = start + (range->len >> sb->s_blocksize_bits) - 1; | ||
| 99 | if (end >= bmp->db_mapsize) | ||
| 100 | end = bmp->db_mapsize - 1; | ||
| 101 | minlen = range->minlen >> sb->s_blocksize_bits; | ||
| 102 | if (minlen <= 0) | ||
| 103 | minlen = 1; | ||
| 104 | |||
| 105 | /** | ||
| 106 | * we trim all ag's within the range | ||
| 107 | */ | ||
| 108 | agno = BLKTOAG(start, JFS_SBI(ip->i_sb)); | ||
| 109 | agno_end = BLKTOAG(end, JFS_SBI(ip->i_sb)); | ||
| 110 | while (agno <= agno_end) { | ||
| 111 | trimmed += dbDiscardAG(ip, agno, minlen); | ||
| 112 | agno++; | ||
| 113 | } | ||
| 114 | range->len = trimmed << sb->s_blocksize_bits; | ||
| 115 | |||
| 116 | return 0; | ||
| 117 | } | ||
diff --git a/fs/jfs/jfs_discard.h b/fs/jfs/jfs_discard.h new file mode 100644 index 000000000000..40d1ee6081a0 --- /dev/null +++ b/fs/jfs/jfs_discard.h | |||
| @@ -0,0 +1,26 @@ | |||
| 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 | #ifndef _H_JFS_DISCARD | ||
| 19 | #define _H_JFS_DISCARD | ||
| 20 | |||
| 21 | struct fstrim_range; | ||
| 22 | |||
| 23 | extern void jfs_issue_discard(struct inode *ip, u64 blkno, u64 nblocks); | ||
| 24 | extern int jfs_ioc_trim(struct inode *ip, struct fstrim_range *range); | ||
| 25 | |||
| 26 | #endif /* _H_JFS_DISCARD */ | ||
diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c index 9cbd11a3f804..174feb6a73c1 100644 --- a/fs/jfs/jfs_dmap.c +++ b/fs/jfs/jfs_dmap.c | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (C) International Business Machines Corp., 2000-2004 | 2 | * Copyright (C) International Business Machines Corp., 2000-2004 |
| 3 | * Portions Copyright (C) Tino Reichardt, 2012 | ||
| 3 | * | 4 | * |
| 4 | * This program is free software; you can redistribute it and/or modify | 5 | * 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 | * it under the terms of the GNU General Public License as published by |
| @@ -25,6 +26,7 @@ | |||
| 25 | #include "jfs_lock.h" | 26 | #include "jfs_lock.h" |
| 26 | #include "jfs_metapage.h" | 27 | #include "jfs_metapage.h" |
| 27 | #include "jfs_debug.h" | 28 | #include "jfs_debug.h" |
| 29 | #include "jfs_discard.h" | ||
| 28 | 30 | ||
| 29 | /* | 31 | /* |
| 30 | * SERIALIZATION of the Block Allocation Map. | 32 | * SERIALIZATION of the Block Allocation Map. |
| @@ -104,7 +106,6 @@ static int dbFreeBits(struct bmap * bmp, struct dmap * dp, s64 blkno, | |||
| 104 | static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno, | 106 | static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno, |
| 105 | int nblocks); | 107 | int nblocks); |
| 106 | static int dbMaxBud(u8 * cp); | 108 | static int dbMaxBud(u8 * cp); |
| 107 | s64 dbMapFileSizeToMapSize(struct inode *ipbmap); | ||
| 108 | static int blkstol2(s64 nb); | 109 | static int blkstol2(s64 nb); |
| 109 | 110 | ||
| 110 | static int cntlz(u32 value); | 111 | static int cntlz(u32 value); |
| @@ -145,7 +146,6 @@ static const s8 budtab[256] = { | |||
| 145 | 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1 | 146 | 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1 |
| 146 | }; | 147 | }; |
| 147 | 148 | ||
| 148 | |||
| 149 | /* | 149 | /* |
| 150 | * NAME: dbMount() | 150 | * NAME: dbMount() |
| 151 | * | 151 | * |
| @@ -310,7 +310,6 @@ int dbSync(struct inode *ipbmap) | |||
| 310 | return (0); | 310 | return (0); |
| 311 | } | 311 | } |
| 312 | 312 | ||
| 313 | |||
| 314 | /* | 313 | /* |
| 315 | * NAME: dbFree() | 314 | * NAME: dbFree() |
| 316 | * | 315 | * |
| @@ -337,6 +336,7 @@ int dbFree(struct inode *ip, s64 blkno, s64 nblocks) | |||
| 337 | s64 lblkno, rem; | 336 | s64 lblkno, rem; |
| 338 | struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap; | 337 | struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap; |
| 339 | struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap; | 338 | struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap; |
| 339 | struct super_block *sb = ipbmap->i_sb; | ||
| 340 | 340 | ||
| 341 | IREAD_LOCK(ipbmap, RDWRLOCK_DMAP); | 341 | IREAD_LOCK(ipbmap, RDWRLOCK_DMAP); |
| 342 | 342 | ||
| @@ -351,6 +351,13 @@ int dbFree(struct inode *ip, s64 blkno, s64 nblocks) | |||
| 351 | return -EIO; | 351 | return -EIO; |
| 352 | } | 352 | } |
| 353 | 353 | ||
| 354 | /** | ||
| 355 | * TRIM the blocks, when mounted with discard option | ||
| 356 | */ | ||
| 357 | if (JFS_SBI(sb)->flag & JFS_DISCARD) | ||
| 358 | if (JFS_SBI(sb)->minblks_trim <= nblocks) | ||
| 359 | jfs_issue_discard(ipbmap, blkno, nblocks); | ||
| 360 | |||
| 354 | /* | 361 | /* |
| 355 | * free the blocks a dmap at a time. | 362 | * free the blocks a dmap at a time. |
| 356 | */ | 363 | */ |
| @@ -1095,7 +1102,6 @@ static int dbExtend(struct inode *ip, s64 blkno, s64 nblocks, s64 addnblocks) | |||
| 1095 | /* we were not successful */ | 1102 | /* we were not successful */ |
| 1096 | release_metapage(mp); | 1103 | release_metapage(mp); |
| 1097 | 1104 | ||
| 1098 | |||
| 1099 | return (rc); | 1105 | return (rc); |
| 1100 | } | 1106 | } |
| 1101 | 1107 | ||
| @@ -1590,6 +1596,117 @@ static int dbAllocAny(struct bmap * bmp, s64 nblocks, int l2nb, s64 * results) | |||
| 1590 | 1596 | ||
| 1591 | 1597 | ||
| 1592 | /* | 1598 | /* |
| 1599 | * NAME: dbDiscardAG() | ||
| 1600 | * | ||
| 1601 | * FUNCTION: attempt to discard (TRIM) all free blocks of specific AG | ||
| 1602 | * | ||
| 1603 | * algorithm: | ||
| 1604 | * 1) allocate blocks, as large as possible and save them | ||
| 1605 | * while holding IWRITE_LOCK on ipbmap | ||
| 1606 | * 2) trim all these saved block/length values | ||
| 1607 | * 3) mark the blocks free again | ||
| 1608 | * | ||
| 1609 | * benefit: | ||
| 1610 | * - we work only on one ag at some time, minimizing how long we | ||
| 1611 | * need to lock ipbmap | ||
| 1612 | * - reading / writing the fs is possible most time, even on | ||
| 1613 | * trimming | ||
| 1614 | * | ||
| 1615 | * downside: | ||
| 1616 | * - we write two times to the dmapctl and dmap pages | ||
| 1617 | * - but for me, this seems the best way, better ideas? | ||
| 1618 | * /TR 2012 | ||
| 1619 | * | ||
| 1620 | * PARAMETERS: | ||
| 1621 | * ip - pointer to in-core inode | ||
| 1622 | * agno - ag to trim | ||
| 1623 | * minlen - minimum value of contiguous blocks | ||
| 1624 | * | ||
| 1625 | * RETURN VALUES: | ||
| 1626 | * s64 - actual number of blocks trimmed | ||
| 1627 | */ | ||
| 1628 | s64 dbDiscardAG(struct inode *ip, int agno, s64 minlen) | ||
| 1629 | { | ||
| 1630 | struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap; | ||
| 1631 | struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap; | ||
| 1632 | s64 nblocks, blkno; | ||
| 1633 | u64 trimmed = 0; | ||
| 1634 | int rc, l2nb; | ||
| 1635 | struct super_block *sb = ipbmap->i_sb; | ||
| 1636 | |||
| 1637 | struct range2trim { | ||
| 1638 | u64 blkno; | ||
| 1639 | u64 nblocks; | ||
| 1640 | } *totrim, *tt; | ||
| 1641 | |||
| 1642 | /* max blkno / nblocks pairs to trim */ | ||
| 1643 | int count = 0, range_cnt; | ||
| 1644 | |||
| 1645 | /* prevent others from writing new stuff here, while trimming */ | ||
| 1646 | IWRITE_LOCK(ipbmap, RDWRLOCK_DMAP); | ||
| 1647 | |||
| 1648 | nblocks = bmp->db_agfree[agno]; | ||
| 1649 | range_cnt = nblocks; | ||
| 1650 | do_div(range_cnt, (int)minlen); | ||
| 1651 | range_cnt = min(range_cnt + 1, 32 * 1024); | ||
| 1652 | totrim = kmalloc(sizeof(struct range2trim) * range_cnt, GFP_NOFS); | ||
| 1653 | if (totrim == NULL) { | ||
| 1654 | jfs_error(bmp->db_ipbmap->i_sb, | ||
| 1655 | "dbDiscardAG: no memory for trim array"); | ||
| 1656 | IWRITE_UNLOCK(ipbmap); | ||
| 1657 | return 0; | ||
| 1658 | } | ||
| 1659 | |||
| 1660 | tt = totrim; | ||
| 1661 | while (nblocks >= minlen) { | ||
| 1662 | l2nb = BLKSTOL2(nblocks); | ||
| 1663 | |||
| 1664 | /* 0 = okay, -EIO = fatal, -ENOSPC -> try smaller block */ | ||
| 1665 | rc = dbAllocAG(bmp, agno, nblocks, l2nb, &blkno); | ||
| 1666 | if (rc == 0) { | ||
| 1667 | tt->blkno = blkno; | ||
| 1668 | tt->nblocks = nblocks; | ||
| 1669 | tt++; count++; | ||
| 1670 | |||
| 1671 | /* the whole ag is free, trim now */ | ||
| 1672 | if (bmp->db_agfree[agno] == 0) | ||
| 1673 | break; | ||
| 1674 | |||
| 1675 | /* give a hint for the next while */ | ||
| 1676 | nblocks = bmp->db_agfree[agno]; | ||
| 1677 | continue; | ||
| 1678 | } else if (rc == -ENOSPC) { | ||
| 1679 | /* search for next smaller log2 block */ | ||
| 1680 | l2nb = BLKSTOL2(nblocks) - 1; | ||
| 1681 | nblocks = 1 << l2nb; | ||
| 1682 | } else { | ||
| 1683 | /* Trim any already allocated blocks */ | ||
| 1684 | jfs_error(bmp->db_ipbmap->i_sb, | ||
| 1685 | "dbDiscardAG: -EIO"); | ||
| 1686 | break; | ||
| 1687 | } | ||
| 1688 | |||
| 1689 | /* check, if our trim array is full */ | ||
| 1690 | if (unlikely(count >= range_cnt - 1)) | ||
| 1691 | break; | ||
| 1692 | } | ||
| 1693 | IWRITE_UNLOCK(ipbmap); | ||
| 1694 | |||
| 1695 | tt->nblocks = 0; /* mark the current end */ | ||
| 1696 | for (tt = totrim; tt->nblocks != 0; tt++) { | ||
| 1697 | /* when mounted with online discard, dbFree() will | ||
| 1698 | * call jfs_issue_discard() itself */ | ||
| 1699 | if (!(JFS_SBI(sb)->flag & JFS_DISCARD)) | ||
| 1700 | jfs_issue_discard(ip, tt->blkno, tt->nblocks); | ||
| 1701 | dbFree(ip, tt->blkno, tt->nblocks); | ||
| 1702 | trimmed += tt->nblocks; | ||
| 1703 | } | ||
| 1704 | kfree(totrim); | ||
| 1705 | |||
| 1706 | return trimmed; | ||
| 1707 | } | ||
| 1708 | |||
| 1709 | /* | ||
| 1593 | * NAME: dbFindCtl() | 1710 | * NAME: dbFindCtl() |
| 1594 | * | 1711 | * |
| 1595 | * FUNCTION: starting at a specified dmap control page level and block | 1712 | * FUNCTION: starting at a specified dmap control page level and block |
diff --git a/fs/jfs/jfs_dmap.h b/fs/jfs/jfs_dmap.h index 6dcb906c55d8..562b9a7e4311 100644 --- a/fs/jfs/jfs_dmap.h +++ b/fs/jfs/jfs_dmap.h | |||
| @@ -311,4 +311,6 @@ extern int dbAllocBottomUp(struct inode *ip, s64 blkno, s64 nblocks); | |||
| 311 | extern int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks); | 311 | extern int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks); |
| 312 | extern void dbFinalizeBmap(struct inode *ipbmap); | 312 | extern void dbFinalizeBmap(struct inode *ipbmap); |
| 313 | extern s64 dbMapFileSizeToMapSize(struct inode *ipbmap); | 313 | extern s64 dbMapFileSizeToMapSize(struct inode *ipbmap); |
| 314 | extern s64 dbDiscardAG(struct inode *ip, int agno, s64 minlen); | ||
| 315 | |||
| 314 | #endif /* _H_JFS_DMAP */ | 316 | #endif /* _H_JFS_DMAP */ |
diff --git a/fs/jfs/jfs_filsys.h b/fs/jfs/jfs_filsys.h index b3f5463fbe52..b67d64671bb4 100644 --- a/fs/jfs/jfs_filsys.h +++ b/fs/jfs/jfs_filsys.h | |||
| @@ -45,6 +45,9 @@ | |||
| 45 | /* mount time flag to disable journaling to disk */ | 45 | /* mount time flag to disable journaling to disk */ |
| 46 | #define JFS_NOINTEGRITY 0x00000040 | 46 | #define JFS_NOINTEGRITY 0x00000040 |
| 47 | 47 | ||
| 48 | /* mount time flag to enable TRIM to ssd disks */ | ||
| 49 | #define JFS_DISCARD 0x00000080 | ||
| 50 | |||
| 48 | /* commit option */ | 51 | /* commit option */ |
| 49 | #define JFS_COMMIT 0x00000f00 /* commit option mask */ | 52 | #define JFS_COMMIT 0x00000f00 /* commit option mask */ |
| 50 | #define JFS_GROUPCOMMIT 0x00000100 /* group (of 1) commit */ | 53 | #define JFS_GROUPCOMMIT 0x00000100 /* group (of 1) commit */ |
diff --git a/fs/jfs/jfs_incore.h b/fs/jfs/jfs_incore.h index 584a4a1a6e81..4fa958ae1986 100644 --- a/fs/jfs/jfs_incore.h +++ b/fs/jfs/jfs_incore.h | |||
| @@ -195,6 +195,7 @@ struct jfs_sb_info { | |||
| 195 | uint uid; /* uid to override on-disk uid */ | 195 | uint uid; /* uid to override on-disk uid */ |
| 196 | uint gid; /* gid to override on-disk gid */ | 196 | uint gid; /* gid to override on-disk gid */ |
| 197 | uint umask; /* umask to override on-disk umask */ | 197 | uint umask; /* umask to override on-disk umask */ |
| 198 | uint minblks_trim; /* minimum blocks, for online trim */ | ||
| 198 | }; | 199 | }; |
| 199 | 200 | ||
| 200 | /* jfs_sb_info commit_state */ | 201 | /* jfs_sb_info commit_state */ |
diff --git a/fs/jfs/super.c b/fs/jfs/super.c index c55c7452d285..6f4ac1c070f0 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c | |||
| @@ -33,6 +33,7 @@ | |||
| 33 | #include <linux/slab.h> | 33 | #include <linux/slab.h> |
| 34 | #include <asm/uaccess.h> | 34 | #include <asm/uaccess.h> |
| 35 | #include <linux/seq_file.h> | 35 | #include <linux/seq_file.h> |
| 36 | #include <linux/blkdev.h> | ||
| 36 | 37 | ||
| 37 | #include "jfs_incore.h" | 38 | #include "jfs_incore.h" |
| 38 | #include "jfs_filsys.h" | 39 | #include "jfs_filsys.h" |
| @@ -100,7 +101,7 @@ void jfs_error(struct super_block *sb, const char * function, ...) | |||
| 100 | vsnprintf(error_buf, sizeof(error_buf), function, args); | 101 | vsnprintf(error_buf, sizeof(error_buf), function, args); |
| 101 | va_end(args); | 102 | va_end(args); |
| 102 | 103 | ||
| 103 | printk(KERN_ERR "ERROR: (device %s): %s\n", sb->s_id, error_buf); | 104 | pr_err("ERROR: (device %s): %s\n", sb->s_id, error_buf); |
| 104 | 105 | ||
| 105 | jfs_handle_error(sb); | 106 | jfs_handle_error(sb); |
| 106 | } | 107 | } |
| @@ -197,7 +198,8 @@ static void jfs_put_super(struct super_block *sb) | |||
| 197 | enum { | 198 | enum { |
| 198 | Opt_integrity, Opt_nointegrity, Opt_iocharset, Opt_resize, | 199 | Opt_integrity, Opt_nointegrity, Opt_iocharset, Opt_resize, |
| 199 | Opt_resize_nosize, Opt_errors, Opt_ignore, Opt_err, Opt_quota, | 200 | Opt_resize_nosize, Opt_errors, Opt_ignore, Opt_err, Opt_quota, |
| 200 | Opt_usrquota, Opt_grpquota, Opt_uid, Opt_gid, Opt_umask | 201 | Opt_usrquota, Opt_grpquota, Opt_uid, Opt_gid, Opt_umask, |
| 202 | Opt_discard, Opt_nodiscard, Opt_discard_minblk | ||
| 201 | }; | 203 | }; |
| 202 | 204 | ||
| 203 | static const match_table_t tokens = { | 205 | static const match_table_t tokens = { |
| @@ -214,6 +216,9 @@ static const match_table_t tokens = { | |||
| 214 | {Opt_uid, "uid=%u"}, | 216 | {Opt_uid, "uid=%u"}, |
| 215 | {Opt_gid, "gid=%u"}, | 217 | {Opt_gid, "gid=%u"}, |
| 216 | {Opt_umask, "umask=%u"}, | 218 | {Opt_umask, "umask=%u"}, |
| 219 | {Opt_discard, "discard"}, | ||
| 220 | {Opt_nodiscard, "nodiscard"}, | ||
| 221 | {Opt_discard_minblk, "discard=%u"}, | ||
| 217 | {Opt_err, NULL} | 222 | {Opt_err, NULL} |
| 218 | }; | 223 | }; |
| 219 | 224 | ||
| @@ -255,8 +260,7 @@ static int parse_options(char *options, struct super_block *sb, s64 *newLVSize, | |||
| 255 | else { | 260 | else { |
| 256 | nls_map = load_nls(args[0].from); | 261 | nls_map = load_nls(args[0].from); |
| 257 | if (!nls_map) { | 262 | if (!nls_map) { |
| 258 | printk(KERN_ERR | 263 | pr_err("JFS: charset not found\n"); |
| 259 | "JFS: charset not found\n"); | ||
| 260 | goto cleanup; | 264 | goto cleanup; |
| 261 | } | 265 | } |
| 262 | } | 266 | } |
| @@ -272,8 +276,7 @@ static int parse_options(char *options, struct super_block *sb, s64 *newLVSize, | |||
| 272 | *newLVSize = sb->s_bdev->bd_inode->i_size >> | 276 | *newLVSize = sb->s_bdev->bd_inode->i_size >> |
| 273 | sb->s_blocksize_bits; | 277 | sb->s_blocksize_bits; |
| 274 | if (*newLVSize == 0) | 278 | if (*newLVSize == 0) |
| 275 | printk(KERN_ERR | 279 | pr_err("JFS: Cannot determine volume size\n"); |
| 276 | "JFS: Cannot determine volume size\n"); | ||
| 277 | break; | 280 | break; |
| 278 | } | 281 | } |
| 279 | case Opt_errors: | 282 | case Opt_errors: |
| @@ -294,8 +297,7 @@ static int parse_options(char *options, struct super_block *sb, s64 *newLVSize, | |||
| 294 | *flag &= ~JFS_ERR_REMOUNT_RO; | 297 | *flag &= ~JFS_ERR_REMOUNT_RO; |
| 295 | *flag |= JFS_ERR_PANIC; | 298 | *flag |= JFS_ERR_PANIC; |
| 296 | } else { | 299 | } else { |
| 297 | printk(KERN_ERR | 300 | pr_err("JFS: %s is an invalid error handler\n", |
| 298 | "JFS: %s is an invalid error handler\n", | ||
| 299 | errors); | 301 | errors); |
| 300 | goto cleanup; | 302 | goto cleanup; |
| 301 | } | 303 | } |
| @@ -314,8 +316,7 @@ static int parse_options(char *options, struct super_block *sb, s64 *newLVSize, | |||
| 314 | case Opt_usrquota: | 316 | case Opt_usrquota: |
| 315 | case Opt_grpquota: | 317 | case Opt_grpquota: |
| 316 | case Opt_quota: | 318 | case Opt_quota: |
| 317 | printk(KERN_ERR | 319 | pr_err("JFS: quota operations not supported\n"); |
| 318 | "JFS: quota operations not supported\n"); | ||
| 319 | break; | 320 | break; |
| 320 | #endif | 321 | #endif |
| 321 | case Opt_uid: | 322 | case Opt_uid: |
| @@ -324,23 +325,61 @@ static int parse_options(char *options, struct super_block *sb, s64 *newLVSize, | |||
| 324 | sbi->uid = simple_strtoul(uid, &uid, 0); | 325 | sbi->uid = simple_strtoul(uid, &uid, 0); |
| 325 | break; | 326 | break; |
| 326 | } | 327 | } |
| 328 | |||
| 327 | case Opt_gid: | 329 | case Opt_gid: |
| 328 | { | 330 | { |
| 329 | char *gid = args[0].from; | 331 | char *gid = args[0].from; |
| 330 | sbi->gid = simple_strtoul(gid, &gid, 0); | 332 | sbi->gid = simple_strtoul(gid, &gid, 0); |
| 331 | break; | 333 | break; |
| 332 | } | 334 | } |
| 335 | |||
| 333 | case Opt_umask: | 336 | case Opt_umask: |
| 334 | { | 337 | { |
| 335 | char *umask = args[0].from; | 338 | char *umask = args[0].from; |
| 336 | sbi->umask = simple_strtoul(umask, &umask, 8); | 339 | sbi->umask = simple_strtoul(umask, &umask, 8); |
| 337 | if (sbi->umask & ~0777) { | 340 | if (sbi->umask & ~0777) { |
| 338 | printk(KERN_ERR | 341 | pr_err("JFS: Invalid value of umask\n"); |
| 339 | "JFS: Invalid value of umask\n"); | ||
| 340 | goto cleanup; | 342 | goto cleanup; |
| 341 | } | 343 | } |
| 342 | break; | 344 | break; |
| 343 | } | 345 | } |
| 346 | |||
| 347 | case Opt_discard: | ||
| 348 | { | ||
| 349 | struct request_queue *q = bdev_get_queue(sb->s_bdev); | ||
| 350 | /* if set to 1, even copying files will cause | ||
| 351 | * trimming :O | ||
| 352 | * -> user has more control over the online trimming | ||
| 353 | */ | ||
| 354 | sbi->minblks_trim = 64; | ||
| 355 | if (blk_queue_discard(q)) { | ||
| 356 | *flag |= JFS_DISCARD; | ||
| 357 | } else { | ||
| 358 | pr_err("JFS: discard option " \ | ||
| 359 | "not supported on device\n"); | ||
| 360 | } | ||
| 361 | break; | ||
| 362 | } | ||
| 363 | |||
| 364 | case Opt_nodiscard: | ||
| 365 | *flag &= ~JFS_DISCARD; | ||
| 366 | break; | ||
| 367 | |||
| 368 | case Opt_discard_minblk: | ||
| 369 | { | ||
| 370 | struct request_queue *q = bdev_get_queue(sb->s_bdev); | ||
| 371 | char *minblks_trim = args[0].from; | ||
| 372 | if (blk_queue_discard(q)) { | ||
| 373 | *flag |= JFS_DISCARD; | ||
| 374 | sbi->minblks_trim = simple_strtoull( | ||
| 375 | minblks_trim, &minblks_trim, 0); | ||
| 376 | } else { | ||
| 377 | pr_err("JFS: discard option " \ | ||
| 378 | "not supported on device\n"); | ||
| 379 | } | ||
| 380 | break; | ||
| 381 | } | ||
| 382 | |||
| 344 | default: | 383 | default: |
| 345 | printk("jfs: Unrecognized mount option \"%s\" " | 384 | printk("jfs: Unrecognized mount option \"%s\" " |
| 346 | " or missing value\n", p); | 385 | " or missing value\n", p); |
| @@ -374,8 +413,8 @@ static int jfs_remount(struct super_block *sb, int *flags, char *data) | |||
| 374 | 413 | ||
| 375 | if (newLVSize) { | 414 | if (newLVSize) { |
| 376 | if (sb->s_flags & MS_RDONLY) { | 415 | if (sb->s_flags & MS_RDONLY) { |
| 377 | printk(KERN_ERR | 416 | pr_err("JFS: resize requires volume" \ |
| 378 | "JFS: resize requires volume to be mounted read-write\n"); | 417 | " to be mounted read-write\n"); |
| 379 | return -EROFS; | 418 | return -EROFS; |
| 380 | } | 419 | } |
| 381 | rc = jfs_extendfs(sb, newLVSize, 0); | 420 | rc = jfs_extendfs(sb, newLVSize, 0); |
| @@ -457,7 +496,7 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent) | |||
| 457 | #endif | 496 | #endif |
| 458 | 497 | ||
| 459 | if (newLVSize) { | 498 | if (newLVSize) { |
| 460 | printk(KERN_ERR "resize option for remount only\n"); | 499 | pr_err("resize option for remount only\n"); |
| 461 | goto out_kfree; | 500 | goto out_kfree; |
| 462 | } | 501 | } |
| 463 | 502 | ||
| @@ -625,6 +664,8 @@ static int jfs_show_options(struct seq_file *seq, struct dentry *root) | |||
| 625 | seq_printf(seq, ",umask=%03o", sbi->umask); | 664 | seq_printf(seq, ",umask=%03o", sbi->umask); |
| 626 | if (sbi->flag & JFS_NOINTEGRITY) | 665 | if (sbi->flag & JFS_NOINTEGRITY) |
| 627 | seq_puts(seq, ",nointegrity"); | 666 | seq_puts(seq, ",nointegrity"); |
| 667 | if (sbi->flag & JFS_DISCARD) | ||
| 668 | seq_printf(seq, ",discard=%u", sbi->minblks_trim); | ||
| 628 | if (sbi->nls_tab) | 669 | if (sbi->nls_tab) |
| 629 | seq_printf(seq, ",iocharset=%s", sbi->nls_tab->charset); | 670 | seq_printf(seq, ",iocharset=%s", sbi->nls_tab->charset); |
| 630 | if (sbi->flag & JFS_ERR_CONTINUE) | 671 | if (sbi->flag & JFS_ERR_CONTINUE) |
