aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Whitehouse <swhiteho@redhat.com>2009-02-09 04:25:01 -0500
committerSteven Whitehouse <steve@dolmen.chygwyn.com>2009-03-24 07:21:20 -0400
commitf15ab5619d8068a321094f4705147764d689e88e (patch)
treec08044e928dff4628b0aa6ea17d6bc5fb922d5dc
parentd8348de06f704fc34d24ec068546ecb1045fc11a (diff)
GFS2: Support generation of discard requests
This patch allows GFS2 to generate discard requests for blocks which are no longer useful to the filesystem (i.e. those which have been freed as the result of an unlink operation). The requests are generated at the time which those blocks become available for reuse in the filesystem. In order to use this new feature, you have to specify the "discard" mount option. The code coalesces adjacent blocks into a single extent when generating the discard requests, thus generating the minimum number. If an error occurs when the request has been sent to the block device, then it will print a message and turn off the requests for that filesystem. If the problem is temporary, then you can use remount to turn the option back on again. There is also a nodiscard mount option so that you can use remount to turn discard requests off, if required. Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
-rw-r--r--fs/gfs2/incore.h2
-rw-r--r--fs/gfs2/mount.c10
-rw-r--r--fs/gfs2/ops_super.c2
-rw-r--r--fs/gfs2/rgrp.c55
4 files changed, 68 insertions, 1 deletions
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index 8fe0675120ac..3f29bd224ba1 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -416,7 +416,7 @@ struct gfs2_args {
416 unsigned int ar_suiddir:1; /* suiddir support */ 416 unsigned int ar_suiddir:1; /* suiddir support */
417 unsigned int ar_data:2; /* ordered/writeback */ 417 unsigned int ar_data:2; /* ordered/writeback */
418 unsigned int ar_meta:1; /* mount metafs */ 418 unsigned int ar_meta:1; /* mount metafs */
419 unsigned int ar_num_glockd; /* Number of glockd threads */ 419 unsigned int ar_discard:1; /* discard requests */
420}; 420};
421 421
422struct gfs2_tune { 422struct gfs2_tune {
diff --git a/fs/gfs2/mount.c b/fs/gfs2/mount.c
index fba502aa8b2d..ee69701a7777 100644
--- a/fs/gfs2/mount.c
+++ b/fs/gfs2/mount.c
@@ -41,6 +41,8 @@ enum {
41 Opt_data_writeback, 41 Opt_data_writeback,
42 Opt_data_ordered, 42 Opt_data_ordered,
43 Opt_meta, 43 Opt_meta,
44 Opt_discard,
45 Opt_nodiscard,
44 Opt_err, 46 Opt_err,
45}; 47};
46 48
@@ -65,6 +67,8 @@ static const match_table_t tokens = {
65 {Opt_data_writeback, "data=writeback"}, 67 {Opt_data_writeback, "data=writeback"},
66 {Opt_data_ordered, "data=ordered"}, 68 {Opt_data_ordered, "data=ordered"},
67 {Opt_meta, "meta"}, 69 {Opt_meta, "meta"},
70 {Opt_discard, "discard"},
71 {Opt_nodiscard, "nodiscard"},
68 {Opt_err, NULL} 72 {Opt_err, NULL}
69}; 73};
70 74
@@ -157,6 +161,12 @@ int gfs2_mount_args(struct gfs2_sbd *sdp, struct gfs2_args *args, char *options)
157 case Opt_meta: 161 case Opt_meta:
158 args->ar_meta = 1; 162 args->ar_meta = 1;
159 break; 163 break;
164 case Opt_discard:
165 args->ar_discard = 1;
166 break;
167 case Opt_nodiscard:
168 args->ar_discard = 0;
169 break;
160 case Opt_err: 170 case Opt_err:
161 default: 171 default:
162 fs_info(sdp, "invalid mount option: %s\n", o); 172 fs_info(sdp, "invalid mount option: %s\n", o);
diff --git a/fs/gfs2/ops_super.c b/fs/gfs2/ops_super.c
index 4ecdad026eaf..458019569dcb 100644
--- a/fs/gfs2/ops_super.c
+++ b/fs/gfs2/ops_super.c
@@ -608,6 +608,8 @@ static int gfs2_show_options(struct seq_file *s, struct vfsmount *mnt)
608 } 608 }
609 seq_printf(s, ",data=%s", state); 609 seq_printf(s, ",data=%s", state);
610 } 610 }
611 if (args->ar_discard)
612 seq_printf(s, ",discard");
611 613
612 return 0; 614 return 0;
613} 615}
diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c
index ba5a021b1c57..789953a2b6a8 100644
--- a/fs/gfs2/rgrp.c
+++ b/fs/gfs2/rgrp.c
@@ -14,6 +14,7 @@
14#include <linux/fs.h> 14#include <linux/fs.h>
15#include <linux/gfs2_ondisk.h> 15#include <linux/gfs2_ondisk.h>
16#include <linux/prefetch.h> 16#include <linux/prefetch.h>
17#include <linux/blkdev.h>
17 18
18#include "gfs2.h" 19#include "gfs2.h"
19#include "incore.h" 20#include "incore.h"
@@ -830,6 +831,58 @@ void gfs2_rgrp_bh_put(struct gfs2_rgrpd *rgd)
830 spin_unlock(&sdp->sd_rindex_spin); 831 spin_unlock(&sdp->sd_rindex_spin);
831} 832}
832 833
834static void gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 offset,
835 const struct gfs2_bitmap *bi)
836{
837 struct super_block *sb = sdp->sd_vfs;
838 struct block_device *bdev = sb->s_bdev;
839 const unsigned int sects_per_blk = sdp->sd_sb.sb_bsize /
840 bdev_hardsect_size(sb->s_bdev);
841 u64 blk;
842 sector_t start;
843 sector_t nr_sects = 0;
844 int rv;
845 unsigned int x;
846
847 for (x = 0; x < bi->bi_len; x++) {
848 const u8 *orig = bi->bi_bh->b_data + bi->bi_offset + x;
849 const u8 *clone = bi->bi_clone + bi->bi_offset + x;
850 u8 diff = ~(*orig | (*orig >> 1)) & (*clone | (*clone >> 1));
851 diff &= 0x55;
852 if (diff == 0)
853 continue;
854 blk = offset + ((bi->bi_start + x) * GFS2_NBBY);
855 blk *= sects_per_blk; /* convert to sectors */
856 while(diff) {
857 if (diff & 1) {
858 if (nr_sects == 0)
859 goto start_new_extent;
860 if ((start + nr_sects) != blk) {
861 rv = blkdev_issue_discard(bdev, start,
862 nr_sects, GFP_NOFS);
863 if (rv)
864 goto fail;
865 nr_sects = 0;
866start_new_extent:
867 start = blk;
868 }
869 nr_sects += sects_per_blk;
870 }
871 diff >>= 2;
872 blk += sects_per_blk;
873 }
874 }
875 if (nr_sects) {
876 rv = blkdev_issue_discard(bdev, start, nr_sects, GFP_NOFS);
877 if (rv)
878 goto fail;
879 }
880 return;
881fail:
882 fs_warn(sdp, "error %d on discard request, turning discards off for this filesystem", rv);
883 sdp->sd_args.ar_discard = 0;
884}
885
833void gfs2_rgrp_repolish_clones(struct gfs2_rgrpd *rgd) 886void gfs2_rgrp_repolish_clones(struct gfs2_rgrpd *rgd)
834{ 887{
835 struct gfs2_sbd *sdp = rgd->rd_sbd; 888 struct gfs2_sbd *sdp = rgd->rd_sbd;
@@ -840,6 +893,8 @@ void gfs2_rgrp_repolish_clones(struct gfs2_rgrpd *rgd)
840 struct gfs2_bitmap *bi = rgd->rd_bits + x; 893 struct gfs2_bitmap *bi = rgd->rd_bits + x;
841 if (!bi->bi_clone) 894 if (!bi->bi_clone)
842 continue; 895 continue;
896 if (sdp->sd_args.ar_discard)
897 gfs2_rgrp_send_discards(sdp, rgd->rd_data0, bi);
843 memcpy(bi->bi_clone + bi->bi_offset, 898 memcpy(bi->bi_clone + bi->bi_offset,
844 bi->bi_bh->b_data + bi->bi_offset, bi->bi_len); 899 bi->bi_bh->b_data + bi->bi_offset, bi->bi_len);
845 } 900 }