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 26ebde77e82..2f94f9ca179 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 a58fa72d7e5..d20d4737b3e 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 f19d1e04a37..bc555ff417e 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 00000000000..9947563e417 --- /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 00000000000..40d1ee6081a --- /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 9cbd11a3f80..174feb6a73c 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 6dcb906c55d..562b9a7e431 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 b3f5463fbe5..b67d64671bb 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 584a4a1a6e8..4fa958ae198 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 c55c7452d28..6f4ac1c070f 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) |