ext4: make trim/discard optional (and off by default)

It is anticipated that when sb_issue_discard starts doing
real work on trim-capable devices, we may see issues.  Make
this mount-time optional, and default it to off until we know
that things are working out OK.

Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
This commit is contained in:
Eric Sandeen 2009-11-19 14:25:42 -05:00 committed by Theodore Ts'o
parent 2bba702d4f
commit 5328e63531
4 changed files with 32 additions and 8 deletions

View file

@ -353,6 +353,12 @@ noauto_da_alloc replacing existing files via patterns such as
system crashes before the delayed allocation system crashes before the delayed allocation
blocks are forced to disk. blocks are forced to disk.
discard Controls whether ext4 should issue discard/TRIM
nodiscard(*) commands to the underlying block device when
blocks are freed. This is useful for SSD devices
and sparse/thinly-provisioned LUNs, but it is off
by default until sufficient testing has been done.
Data Mode Data Mode
========= =========
There are 3 different data modes: There are 3 different data modes:

View file

@ -750,6 +750,7 @@ struct ext4_inode_info {
#define EXT4_MOUNT_DELALLOC 0x8000000 /* Delalloc support */ #define EXT4_MOUNT_DELALLOC 0x8000000 /* Delalloc support */
#define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */ #define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */
#define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000 /* Block validity checking */ #define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000 /* Block validity checking */
#define EXT4_MOUNT_DISCARD 0x40000000 /* Issue DISCARD requests */
#define clear_opt(o, opt) o &= ~EXT4_MOUNT_##opt #define clear_opt(o, opt) o &= ~EXT4_MOUNT_##opt
#define set_opt(o, opt) o |= EXT4_MOUNT_##opt #define set_opt(o, opt) o |= EXT4_MOUNT_##opt

View file

@ -2529,7 +2529,6 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn)
struct ext4_group_info *db; struct ext4_group_info *db;
int err, count = 0, count2 = 0; int err, count = 0, count2 = 0;
struct ext4_free_data *entry; struct ext4_free_data *entry;
ext4_fsblk_t discard_block;
struct list_head *l, *ltmp; struct list_head *l, *ltmp;
list_for_each_safe(l, ltmp, &txn->t_private_list) { list_for_each_safe(l, ltmp, &txn->t_private_list) {
@ -2559,13 +2558,19 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn)
page_cache_release(e4b.bd_bitmap_page); page_cache_release(e4b.bd_bitmap_page);
} }
ext4_unlock_group(sb, entry->group); ext4_unlock_group(sb, entry->group);
discard_block = (ext4_fsblk_t) entry->group * EXT4_BLOCKS_PER_GROUP(sb) if (test_opt(sb, DISCARD)) {
ext4_fsblk_t discard_block;
struct ext4_super_block *es = EXT4_SB(sb)->s_es;
discard_block = (ext4_fsblk_t)entry->group *
EXT4_BLOCKS_PER_GROUP(sb)
+ entry->start_blk + entry->start_blk
+ le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block); + le32_to_cpu(es->s_first_data_block);
trace_ext4_discard_blocks(sb, (unsigned long long)discard_block, trace_ext4_discard_blocks(sb,
(unsigned long long)discard_block,
entry->count); entry->count);
sb_issue_discard(sb, discard_block, entry->count); sb_issue_discard(sb, discard_block, entry->count);
}
kmem_cache_free(ext4_free_ext_cachep, entry); kmem_cache_free(ext4_free_ext_cachep, entry);
ext4_mb_release_desc(&e4b); ext4_mb_release_desc(&e4b);
} }

View file

@ -899,6 +899,9 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs)
if (test_opt(sb, NO_AUTO_DA_ALLOC)) if (test_opt(sb, NO_AUTO_DA_ALLOC))
seq_puts(seq, ",noauto_da_alloc"); seq_puts(seq, ",noauto_da_alloc");
if (test_opt(sb, DISCARD))
seq_puts(seq, ",discard");
ext4_show_quota_options(seq, sb); ext4_show_quota_options(seq, sb);
return 0; return 0;
@ -1079,7 +1082,8 @@ enum {
Opt_usrquota, Opt_grpquota, Opt_i_version, Opt_usrquota, Opt_grpquota, Opt_i_version,
Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_stripe, Opt_delalloc, Opt_nodelalloc,
Opt_block_validity, Opt_noblock_validity, Opt_block_validity, Opt_noblock_validity,
Opt_inode_readahead_blks, Opt_journal_ioprio Opt_inode_readahead_blks, Opt_journal_ioprio,
Opt_discard, Opt_nodiscard,
}; };
static const match_table_t tokens = { static const match_table_t tokens = {
@ -1144,6 +1148,8 @@ static const match_table_t tokens = {
{Opt_auto_da_alloc, "auto_da_alloc=%u"}, {Opt_auto_da_alloc, "auto_da_alloc=%u"},
{Opt_auto_da_alloc, "auto_da_alloc"}, {Opt_auto_da_alloc, "auto_da_alloc"},
{Opt_noauto_da_alloc, "noauto_da_alloc"}, {Opt_noauto_da_alloc, "noauto_da_alloc"},
{Opt_discard, "discard"},
{Opt_nodiscard, "nodiscard"},
{Opt_err, NULL}, {Opt_err, NULL},
}; };
@ -1565,6 +1571,12 @@ static int parse_options(char *options, struct super_block *sb,
else else
set_opt(sbi->s_mount_opt,NO_AUTO_DA_ALLOC); set_opt(sbi->s_mount_opt,NO_AUTO_DA_ALLOC);
break; break;
case Opt_discard:
set_opt(sbi->s_mount_opt, DISCARD);
break;
case Opt_nodiscard:
clear_opt(sbi->s_mount_opt, DISCARD);
break;
default: default:
ext4_msg(sb, KERN_ERR, ext4_msg(sb, KERN_ERR,
"Unrecognized mount option \"%s\" " "Unrecognized mount option \"%s\" "