]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blobdiff - src/patches/suse-2.6.27.31/patches.fixes/block-discard-requests
Imported linux-2.6.27.39 suse/xen patches.
[people/pmueller/ipfire-2.x.git] / src / patches / suse-2.6.27.31 / patches.fixes / block-discard-requests
diff --git a/src/patches/suse-2.6.27.31/patches.fixes/block-discard-requests b/src/patches/suse-2.6.27.31/patches.fixes/block-discard-requests
deleted file mode 100644 (file)
index 2abe841..0000000
+++ /dev/null
@@ -1,782 +0,0 @@
-From b24db85c3e8a53ff0d3255b22e8e8b674572bdbc Mon Sep 17 00:00:00 2001
-From: Hannes Reinecke <hare@suse.de>
-Date: Thu, 23 Oct 2008 13:53:12 +0200
-Subject: [PATCH] Implement block discard
-
-Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
-Signed-off-by: Hannes Reinecke <hare@suse.de>
----
- block/blk-barrier.c          |   69 +++++++++++++++++++++++++++++++++++++++
- block/blk-core.c             |   34 +++++++++++++------
- block/blk-merge.c            |   27 +++++++++------
- block/blk-settings.c         |   17 +++++++++
- block/blktrace.c             |   29 ++++------------
- block/compat_ioctl.c         |    1 
- block/elevator.c             |   12 +++++-
- block/ioctl.c                |   76 +++++++++++++++++++++++++++++++++++++++++++
- drivers/mtd/ftl.c            |   24 +++++++++++++
- drivers/mtd/mtd_blkdevs.c    |   16 +++++++++
- fs/fat/fatent.c              |   14 +++++++
- include/linux/bio.h          |   10 +++--
- include/linux/blkdev.h       |   22 +++++++++++-
- include/linux/blktrace_api.h |    6 ++-
- include/linux/fs.h           |    5 ++
- include/linux/mtd/blktrans.h |    2 +
- 16 files changed, 314 insertions(+), 50 deletions(-)
-
---- a/block/blk-barrier.c
-+++ b/block/blk-barrier.c
-@@ -315,3 +315,72 @@ int blkdev_issue_flush(struct block_devi
-       return ret;
- }
- EXPORT_SYMBOL(blkdev_issue_flush);
-+
-+static void blkdev_discard_end_io(struct bio *bio, int err)
-+{
-+      if (err) {
-+              if (err == -EOPNOTSUPP)
-+                      set_bit(BIO_EOPNOTSUPP, &bio->bi_flags);
-+              clear_bit(BIO_UPTODATE, &bio->bi_flags);
-+      }
-+
-+      bio_put(bio);
-+}
-+
-+/**
-+ * blkdev_issue_discard - queue a discard
-+ * @bdev:     blockdev to issue discard for
-+ * @sector:   start sector
-+ * @nr_sects: number of sectors to discard
-+ *
-+ * Description:
-+ *    Issue a discard request for the sectors in question. Does not wait.
-+ */
-+int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
-+                       unsigned nr_sects)
-+{
-+      struct request_queue *q;
-+      struct bio *bio;
-+      int ret = 0;
-+
-+      if (bdev->bd_disk == NULL)
-+              return -ENXIO;
-+
-+      q = bdev_get_queue(bdev);
-+      if (!q)
-+              return -ENXIO;
-+
-+      if (!q->prepare_discard_fn)
-+              return -EOPNOTSUPP;
-+
-+      while (nr_sects && !ret) {
-+              bio = bio_alloc(GFP_KERNEL, 0);
-+              if (!bio)
-+                      return -ENOMEM;
-+
-+              bio->bi_end_io = blkdev_discard_end_io;
-+              bio->bi_bdev = bdev;
-+
-+              bio->bi_sector = sector;
-+
-+              if (nr_sects > q->max_hw_sectors) {
-+                      bio->bi_size = q->max_hw_sectors << 9;
-+                      nr_sects -= q->max_hw_sectors;
-+                      sector += q->max_hw_sectors;
-+              } else {
-+                      bio->bi_size = nr_sects << 9;
-+                      nr_sects = 0;
-+              }
-+              bio_get(bio);
-+              submit_bio(DISCARD_BARRIER, bio);
-+
-+              /* Check if it failed immediately */
-+              if (bio_flagged(bio, BIO_EOPNOTSUPP))
-+                      ret = -EOPNOTSUPP;
-+              else if (!bio_flagged(bio, BIO_UPTODATE))
-+                      ret = -EIO;
-+              bio_put(bio);
-+      }
-+      return ret;
-+}
-+EXPORT_SYMBOL(blkdev_issue_discard);
---- a/block/blk-core.c
-+++ b/block/blk-core.c
-@@ -1077,7 +1077,12 @@ void init_request_from_bio(struct reques
-       /*
-        * REQ_BARRIER implies no merging, but lets make it explicit
-        */
--      if (unlikely(bio_barrier(bio)))
-+      if (unlikely(bio_discard(bio))) {
-+              req->cmd_flags |= REQ_DISCARD;
-+              if (bio_barrier(bio))
-+                      req->cmd_flags |= REQ_SOFTBARRIER;
-+              req->q->prepare_discard_fn(req->q, req);
-+      } else if (unlikely(bio_barrier(bio)))
-               req->cmd_flags |= (REQ_HARDBARRIER | REQ_NOMERGE);
-       if (bio_sync(bio))
-@@ -1095,7 +1100,7 @@ void init_request_from_bio(struct reques
- static int __make_request(struct request_queue *q, struct bio *bio)
- {
-       struct request *req;
--      int el_ret, nr_sectors, barrier, err;
-+      int el_ret, nr_sectors, barrier, discard, err;
-       const unsigned short prio = bio_prio(bio);
-       const int sync = bio_sync(bio);
-       int rw_flags;
-@@ -1110,7 +1115,14 @@ static int __make_request(struct request
-       blk_queue_bounce(q, &bio);
-       barrier = bio_barrier(bio);
--      if (unlikely(barrier) && (q->next_ordered == QUEUE_ORDERED_NONE)) {
-+      if (unlikely(barrier) && bio_has_data(bio) &&
-+          (q->next_ordered == QUEUE_ORDERED_NONE)) {
-+              err = -EOPNOTSUPP;
-+              goto end_io;
-+      }
-+
-+      discard = bio_discard(bio);
-+      if (unlikely(discard) && !q->prepare_discard_fn) {
-               err = -EOPNOTSUPP;
-               goto end_io;
-       }
-@@ -1405,7 +1417,8 @@ end_io:
-               if (bio_check_eod(bio, nr_sectors))
-                       goto end_io;
--              if (bio_empty_barrier(bio) && !q->prepare_flush_fn) {
-+              if ((bio_empty_barrier(bio) && !q->prepare_flush_fn) ||
-+                  (bio_discard(bio) && !q->prepare_discard_fn)) {
-                       err = -EOPNOTSUPP;
-                       goto end_io;
-               }
-@@ -1487,7 +1500,6 @@ void submit_bio(int rw, struct bio *bio)
-        * go through the normal accounting stuff before submission.
-        */
-       if (bio_has_data(bio)) {
--
-               if (rw & WRITE) {
-                       count_vm_events(PGPGOUT, count);
-               } else {
-@@ -1881,7 +1893,7 @@ static int blk_end_io(struct request *rq
-       struct request_queue *q = rq->q;
-       unsigned long flags = 0UL;
--      if (bio_has_data(rq->bio)) {
-+      if (bio_has_data(rq->bio) || blk_discard_rq(rq)) {
-               if (__end_that_request_first(rq, error, nr_bytes))
-                       return 1;
-@@ -1939,7 +1951,7 @@ EXPORT_SYMBOL_GPL(blk_end_request);
-  **/
- int __blk_end_request(struct request *rq, int error, unsigned int nr_bytes)
- {
--      if (bio_has_data(rq->bio) &&
-+      if ((bio_has_data(rq->bio) || blk_discard_rq(rq)) &&
-           __end_that_request_first(rq, error, nr_bytes))
-               return 1;
-@@ -2012,12 +2024,14 @@ void blk_rq_bio_prep(struct request_queu
-          we want BIO_RW_AHEAD (bit 1) to imply REQ_FAILFAST (bit 1). */
-       rq->cmd_flags |= (bio->bi_rw & 3);
--      rq->nr_phys_segments = bio_phys_segments(q, bio);
--      rq->nr_hw_segments = bio_hw_segments(q, bio);
-+      if (bio_has_data(bio)) {
-+              rq->nr_phys_segments = bio_phys_segments(q, bio);
-+              rq->nr_hw_segments = bio_hw_segments(q, bio);
-+              rq->buffer = bio_data(bio);
-+      }
-       rq->current_nr_sectors = bio_cur_sectors(bio);
-       rq->hard_cur_sectors = rq->current_nr_sectors;
-       rq->hard_nr_sectors = rq->nr_sectors = bio_sectors(bio);
--      rq->buffer = bio_data(bio);
-       rq->data_len = bio->bi_size;
-       rq->bio = rq->biotail = bio;
---- a/block/blk-merge.c
-+++ b/block/blk-merge.c
-@@ -11,7 +11,7 @@
- void blk_recalc_rq_sectors(struct request *rq, int nsect)
- {
--      if (blk_fs_request(rq)) {
-+      if (blk_fs_request(rq) || blk_discard_rq(rq)) {
-               rq->hard_sector += nsect;
-               rq->hard_nr_sectors -= nsect;
-@@ -138,14 +138,18 @@ static int blk_phys_contig_segment(struc
-       if (!test_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags))
-               return 0;
--      if (!BIOVEC_PHYS_MERGEABLE(__BVEC_END(bio), __BVEC_START(nxt)))
--              return 0;
-       if (bio->bi_seg_back_size + nxt->bi_seg_front_size >
-           q->max_segment_size)
-               return 0;
-+      if (!bio_has_data(bio))
-+              return 1;
-+
-+      if (!BIOVEC_PHYS_MERGEABLE(__BVEC_END(bio), __BVEC_START(nxt)))
-+              return 0;
-+
-       /*
--       * bio and nxt are contigous in memory, check if the queue allows
-+       * bio and nxt are contiguous in memory, check if the queue allows
-        * these two to be merged into one
-        */
-       if (BIO_SEG_BOUNDARY(q, bio, nxt))
-@@ -161,8 +165,9 @@ static int blk_hw_contig_segment(struct 
-               blk_recount_segments(q, bio);
-       if (!bio_flagged(nxt, BIO_SEG_VALID))
-               blk_recount_segments(q, nxt);
--      if (!BIOVEC_VIRT_MERGEABLE(__BVEC_END(bio), __BVEC_START(nxt)) ||
--          BIOVEC_VIRT_OVERSIZE(bio->bi_hw_back_size + nxt->bi_hw_front_size))
-+      if (bio_has_data(bio) &&
-+          (!BIOVEC_VIRT_MERGEABLE(__BVEC_END(bio), __BVEC_START(nxt)) ||
-+           BIOVEC_VIRT_OVERSIZE(bio->bi_hw_back_size + nxt->bi_hw_front_size)))
-               return 0;
-       if (bio->bi_hw_back_size + nxt->bi_hw_front_size > q->max_segment_size)
-               return 0;
-@@ -325,8 +330,9 @@ int ll_back_merge_fn(struct request_queu
-       if (!bio_flagged(bio, BIO_SEG_VALID))
-               blk_recount_segments(q, bio);
-       len = req->biotail->bi_hw_back_size + bio->bi_hw_front_size;
--      if (BIOVEC_VIRT_MERGEABLE(__BVEC_END(req->biotail), __BVEC_START(bio))
--          && !BIOVEC_VIRT_OVERSIZE(len)) {
-+      if (!bio_has_data(bio) ||
-+          (BIOVEC_VIRT_MERGEABLE(__BVEC_END(req->biotail), __BVEC_START(bio))
-+           && !BIOVEC_VIRT_OVERSIZE(len))) {
-               int mergeable =  ll_new_mergeable(q, req, bio);
-               if (mergeable) {
-@@ -364,8 +370,9 @@ int ll_front_merge_fn(struct request_que
-               blk_recount_segments(q, bio);
-       if (!bio_flagged(req->bio, BIO_SEG_VALID))
-               blk_recount_segments(q, req->bio);
--      if (BIOVEC_VIRT_MERGEABLE(__BVEC_END(bio), __BVEC_START(req->bio)) &&
--          !BIOVEC_VIRT_OVERSIZE(len)) {
-+      if (!bio_has_data(bio) ||
-+          (BIOVEC_VIRT_MERGEABLE(__BVEC_END(bio), __BVEC_START(req->bio)) &&
-+           !BIOVEC_VIRT_OVERSIZE(len))) {
-               int mergeable =  ll_new_mergeable(q, req, bio);
-               if (mergeable) {
---- a/block/blk-settings.c
-+++ b/block/blk-settings.c
-@@ -33,6 +33,23 @@ void blk_queue_prep_rq(struct request_qu
- EXPORT_SYMBOL(blk_queue_prep_rq);
- /**
-+ * blk_queue_set_discard - set a discard_sectors function for queue
-+ * @q:                queue
-+ * @dfn:      prepare_discard function
-+ *
-+ * It's possible for a queue to register a discard callback which is used
-+ * to transform a discard request into the appropriate type for the
-+ * hardware. If none is registered, then discard requests are failed
-+ * with %EOPNOTSUPP.
-+ *
-+ */
-+void blk_queue_set_discard(struct request_queue *q, prepare_discard_fn *dfn)
-+{
-+      q->prepare_discard_fn = dfn;
-+}
-+EXPORT_SYMBOL(blk_queue_set_discard);
-+
-+/**
-  * blk_queue_merge_bvec - set a merge_bvec function for queue
-  * @q:                queue
-  * @mbfn:     merge_bvec_fn
---- a/block/blktrace.c
-+++ b/block/blktrace.c
-@@ -111,23 +111,9 @@ static int act_log_check(struct blk_trac
-  */
- static u32 ddir_act[2] __read_mostly = { BLK_TC_ACT(BLK_TC_READ), BLK_TC_ACT(BLK_TC_WRITE) };
--/*
-- * Bio action bits of interest
-- */
--static u32 bio_act[9] __read_mostly = { 0, BLK_TC_ACT(BLK_TC_BARRIER), BLK_TC_ACT(BLK_TC_SYNC), 0, BLK_TC_ACT(BLK_TC_AHEAD), 0, 0, 0, BLK_TC_ACT(BLK_TC_META) };
--
--/*
-- * More could be added as needed, taking care to increment the decrementer
-- * to get correct indexing
-- */
--#define trace_barrier_bit(rw) \
--      (((rw) & (1 << BIO_RW_BARRIER)) >> (BIO_RW_BARRIER - 0))
--#define trace_sync_bit(rw)    \
--      (((rw) & (1 << BIO_RW_SYNC)) >> (BIO_RW_SYNC - 1))
--#define trace_ahead_bit(rw)   \
--      (((rw) & (1 << BIO_RW_AHEAD)) << (2 - BIO_RW_AHEAD))
--#define trace_meta_bit(rw)    \
--      (((rw) & (1 << BIO_RW_META)) >> (BIO_RW_META - 3))
-+/* The ilog2() calls fall out because they're constant */
-+#define MASK_TC_BIT(rw, __name) ( (rw & (1 << BIO_RW_ ## __name)) << \
-+        (ilog2(BLK_TC_ ## __name) + BLK_TC_SHIFT - BIO_RW_ ## __name) )
- /*
-  * The worker for the various blk_add_trace*() types. Fills out a
-@@ -147,10 +133,11 @@ void __blk_add_trace(struct blk_trace *b
-               return;
-       what |= ddir_act[rw & WRITE];
--      what |= bio_act[trace_barrier_bit(rw)];
--      what |= bio_act[trace_sync_bit(rw)];
--      what |= bio_act[trace_ahead_bit(rw)];
--      what |= bio_act[trace_meta_bit(rw)];
-+      what |= MASK_TC_BIT(rw, BARRIER);
-+      what |= MASK_TC_BIT(rw, SYNC);
-+      what |= MASK_TC_BIT(rw, AHEAD);
-+      what |= MASK_TC_BIT(rw, META);
-+      what |= MASK_TC_BIT(rw, DISCARD);
-       pid = tsk->pid;
-       if (unlikely(act_log_check(bt, what, sector, pid)))
---- a/block/compat_ioctl.c
-+++ b/block/compat_ioctl.c
-@@ -788,6 +788,7 @@ long compat_blkdev_ioctl(struct file *fi
-               return compat_hdio_getgeo(disk, bdev, compat_ptr(arg));
-       case BLKFLSBUF:
-       case BLKROSET:
-+      case BLKDISCARD:
-       /*
-        * the ones below are implemented in blkdev_locked_ioctl,
-        * but we call blkdev_ioctl, which gets the lock for us
---- a/block/elevator.c
-+++ b/block/elevator.c
-@@ -75,6 +75,12 @@ int elv_rq_merge_ok(struct request *rq, 
-               return 0;
-       /*
-+       * Don't merge file system requests and discard requests
-+       */
-+      if (bio_discard(bio) != bio_discard(rq->bio))
-+              return 0;
-+
-+      /*
-        * different data direction or already started, don't merge
-        */
-       if (bio_data_dir(bio) != rq_data_dir(rq))
-@@ -438,6 +444,8 @@ void elv_dispatch_sort(struct request_qu
-       list_for_each_prev(entry, &q->queue_head) {
-               struct request *pos = list_entry_rq(entry);
-+              if (blk_discard_rq(rq) != blk_discard_rq(pos))
-+                      break;
-               if (rq_data_dir(rq) != rq_data_dir(pos))
-                       break;
-               if (pos->cmd_flags & stop_flags)
-@@ -607,7 +615,7 @@ void elv_insert(struct request_queue *q,
-               break;
-       case ELEVATOR_INSERT_SORT:
--              BUG_ON(!blk_fs_request(rq));
-+              BUG_ON(!blk_fs_request(rq) && !blk_discard_rq(rq));
-               rq->cmd_flags |= REQ_SORTED;
-               q->nr_sorted++;
-               if (rq_mergeable(rq)) {
-@@ -692,7 +700,7 @@ void __elv_add_request(struct request_qu
-                * this request is scheduling boundary, update
-                * end_sector
-                */
--              if (blk_fs_request(rq)) {
-+              if (blk_fs_request(rq) || blk_discard_rq(rq)) {
-                       q->end_sector = rq_end_sector(rq);
-                       q->boundary_rq = rq;
-               }
---- a/block/ioctl.c
-+++ b/block/ioctl.c
-@@ -111,6 +111,69 @@ static int blkdev_reread_part(struct blo
-       return res;
- }
-+static void blk_ioc_discard_endio(struct bio *bio, int err)
-+{
-+      if (err) {
-+              if (err == -EOPNOTSUPP)
-+                      set_bit(BIO_EOPNOTSUPP, &bio->bi_flags);
-+              clear_bit(BIO_UPTODATE, &bio->bi_flags);
-+      }
-+      complete(bio->bi_private);
-+}
-+
-+static int blk_ioctl_discard(struct block_device *bdev, uint64_t start,
-+                           uint64_t len)
-+{
-+      struct request_queue *q = bdev_get_queue(bdev);
-+      int ret = 0;
-+
-+      if (start & 511)
-+              return -EINVAL;
-+      if (len & 511)
-+              return -EINVAL;
-+      start >>= 9;
-+      len >>= 9;
-+
-+      if (start + len > (bdev->bd_inode->i_size >> 9))
-+              return -EINVAL;
-+
-+      if (!q->prepare_discard_fn)
-+              return -EOPNOTSUPP;
-+
-+      while (len && !ret) {
-+              DECLARE_COMPLETION_ONSTACK(wait);
-+              struct bio *bio;
-+
-+              bio = bio_alloc(GFP_KERNEL, 0);
-+              if (!bio)
-+                      return -ENOMEM;
-+
-+              bio->bi_end_io = blk_ioc_discard_endio;
-+              bio->bi_bdev = bdev;
-+              bio->bi_private = &wait;
-+              bio->bi_sector = start;
-+
-+              if (len > q->max_hw_sectors) {
-+                      bio->bi_size = q->max_hw_sectors << 9;
-+                      len -= q->max_hw_sectors;
-+                      start += q->max_hw_sectors;
-+              } else {
-+                      bio->bi_size = len << 9;
-+                      len = 0;
-+              }
-+              submit_bio(DISCARD_NOBARRIER, bio);
-+
-+              wait_for_completion(&wait);
-+
-+              if (bio_flagged(bio, BIO_EOPNOTSUPP))
-+                      ret = -EOPNOTSUPP;
-+              else if (!bio_flagged(bio, BIO_UPTODATE))
-+                      ret = -EIO;
-+              bio_put(bio);
-+      }
-+      return ret;
-+}
-+
- static int put_ushort(unsigned long arg, unsigned short val)
- {
-       return put_user(val, (unsigned short __user *)arg);
-@@ -258,6 +321,19 @@ int blkdev_ioctl(struct inode *inode, st
-               set_device_ro(bdev, n);
-               unlock_kernel();
-               return 0;
-+
-+      case BLKDISCARD: {
-+              uint64_t range[2];
-+
-+              if (!(file->f_mode & FMODE_WRITE))
-+                      return -EBADF;
-+
-+              if (copy_from_user(range, (void __user *)arg, sizeof(range)))
-+                      return -EFAULT;
-+
-+              return blk_ioctl_discard(bdev, range[0], range[1]);
-+      }
-+
-       case HDIO_GETGEO: {
-               struct hd_geometry geo;
---- a/drivers/mtd/ftl.c
-+++ b/drivers/mtd/ftl.c
-@@ -1005,6 +1005,29 @@ static int ftl_writesect(struct mtd_blkt
-       return ftl_write((void *)dev, buf, block, 1);
- }
-+static int ftl_discardsect(struct mtd_blktrans_dev *dev,
-+                         unsigned long sector, unsigned nr_sects)
-+{
-+      partition_t *part = (void *)dev;
-+      uint32_t bsize = 1 << part->header.EraseUnitSize;
-+
-+      DEBUG(1, "FTL erase sector %ld for %d sectors\n",
-+            sector, nr_sects);
-+
-+      while (nr_sects) {
-+              uint32_t old_addr = part->VirtualBlockMap[sector];
-+              if (old_addr != 0xffffffff) {
-+                      part->VirtualBlockMap[sector] = 0xffffffff;
-+                      part->EUNInfo[old_addr/bsize].Deleted++;
-+                      if (set_bam_entry(part, old_addr, 0))
-+                              return -EIO;
-+              }
-+              nr_sects--;
-+              sector++;
-+      }
-+
-+      return 0;
-+}
- /*====================================================================*/
- static void ftl_freepart(partition_t *part)
-@@ -1069,6 +1092,7 @@ static struct mtd_blktrans_ops ftl_tr = 
-       .blksize        = SECTOR_SIZE,
-       .readsect       = ftl_readsect,
-       .writesect      = ftl_writesect,
-+      .discard        = ftl_discardsect,
-       .getgeo         = ftl_getgeo,
-       .add_mtd        = ftl_add_mtd,
-       .remove_dev     = ftl_remove_dev,
---- a/drivers/mtd/mtd_blkdevs.c
-+++ b/drivers/mtd/mtd_blkdevs.c
-@@ -32,6 +32,14 @@ struct mtd_blkcore_priv {
-       spinlock_t queue_lock;
- };
-+static int blktrans_discard_request(struct request_queue *q,
-+                                  struct request *req)
-+{
-+      req->cmd_type = REQ_TYPE_LINUX_BLOCK;
-+      req->cmd[0] = REQ_LB_OP_DISCARD;
-+      return 0;
-+}
-+
- static int do_blktrans_request(struct mtd_blktrans_ops *tr,
-                              struct mtd_blktrans_dev *dev,
-                              struct request *req)
-@@ -44,6 +52,10 @@ static int do_blktrans_request(struct mt
-       buf = req->buffer;
-+      if (req->cmd_type == REQ_TYPE_LINUX_BLOCK &&
-+          req->cmd[0] == REQ_LB_OP_DISCARD)
-+              return !tr->discard(dev, block, nsect);
-+
-       if (!blk_fs_request(req))
-               return 0;
-@@ -367,6 +379,10 @@ int register_mtd_blktrans(struct mtd_blk
-       tr->blkcore_priv->rq->queuedata = tr;
-       blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize);
-+      if (tr->discard)
-+              blk_queue_set_discard(tr->blkcore_priv->rq,
-+                                    blktrans_discard_request);
-+
-       tr->blkshift = ffs(tr->blksize) - 1;
-       tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
---- a/fs/fat/fatent.c
-+++ b/fs/fat/fatent.c
-@@ -6,6 +6,7 @@
- #include <linux/module.h>
- #include <linux/fs.h>
- #include <linux/msdos_fs.h>
-+#include <linux/blkdev.h>
- struct fatent_operations {
-       void (*ent_blocknr)(struct super_block *, int, int *, sector_t *);
-@@ -535,6 +536,7 @@ int fat_free_clusters(struct inode *inod
-       struct fat_entry fatent;
-       struct buffer_head *bhs[MAX_BUF_PER_PAGE];
-       int i, err, nr_bhs;
-+      int first_cl = cluster;
-       nr_bhs = 0;
-       fatent_init(&fatent);
-@@ -551,6 +553,18 @@ int fat_free_clusters(struct inode *inod
-                       goto error;
-               }
-+              /*
-+               * Issue discard for the sectors we no longer care about,
-+               * batching contiguous clusters into one request
-+               */
-+              if (cluster != fatent.entry + 1) {
-+                      int nr_clus = fatent.entry - first_cl + 1;
-+
-+                      sb_issue_discard(sb, fat_clus_to_blknr(sbi, first_cl),
-+                                       nr_clus * sbi->sec_per_clus);
-+                      first_cl = cluster;
-+              }
-+
-               ops->ent_put(&fatent, FAT_ENT_FREE);
-               if (sbi->free_clusters != -1) {
-                       sbi->free_clusters++;
---- a/include/linux/bio.h
-+++ b/include/linux/bio.h
-@@ -156,6 +156,8 @@ struct bio {
-  * bit 2 -- barrier
-  * bit 3 -- fail fast, don't want low level driver retries
-  * bit 4 -- synchronous I/O hint: the block layer will unplug immediately
-+ * bit 5 -- metadata request
-+ * bit 6 -- discard sectors
-  */
- #define BIO_RW                0       /* Must match RW in req flags (blkdev.h) */
- #define BIO_RW_AHEAD  1       /* Must match FAILFAST in req flags */
-@@ -163,6 +165,7 @@ struct bio {
- #define BIO_RW_FAILFAST       3
- #define BIO_RW_SYNC   4
- #define BIO_RW_META   5
-+#define BIO_RW_DISCARD        6
- /*
-  * upper 16 bits of bi_rw define the io priority of this bio
-@@ -192,14 +195,15 @@ struct bio {
- #define bio_failfast(bio)     ((bio)->bi_rw & (1 << BIO_RW_FAILFAST))
- #define bio_rw_ahead(bio)     ((bio)->bi_rw & (1 << BIO_RW_AHEAD))
- #define bio_rw_meta(bio)      ((bio)->bi_rw & (1 << BIO_RW_META))
--#define bio_empty_barrier(bio)        (bio_barrier(bio) && !bio_has_data(bio))
-+#define bio_discard(bio)      ((bio)->bi_rw & (1 << BIO_RW_DISCARD))
-+#define bio_empty_barrier(bio)        (bio_barrier(bio) && !bio_has_data(bio) && !bio_discard(bio))
- static inline unsigned int bio_cur_sectors(struct bio *bio)
- {
-       if (bio->bi_vcnt)
-               return bio_iovec(bio)->bv_len >> 9;
--
--      return 0;
-+      else /* dataless requests such as discard */
-+              return bio->bi_size >> 9;
- }
- static inline void *bio_data(struct bio *bio)
---- a/include/linux/blkdev.h
-+++ b/include/linux/blkdev.h
-@@ -81,6 +81,7 @@ enum {
-        */
-       REQ_LB_OP_EJECT = 0x40,         /* eject request */
-       REQ_LB_OP_FLUSH = 0x41,         /* flush device */
-+      REQ_LB_OP_DISCARD = 0x42,       /* discard sectors */
- };
- /*
-@@ -89,6 +90,7 @@ enum {
- enum rq_flag_bits {
-       __REQ_RW,               /* not set, read. set, write */
-       __REQ_FAILFAST,         /* no low level driver retries */
-+      __REQ_DISCARD,          /* request to discard sectors */
-       __REQ_SORTED,           /* elevator knows about this request */
-       __REQ_SOFTBARRIER,      /* may not be passed by ioscheduler */
-       __REQ_HARDBARRIER,      /* may not be passed by drive either */
-@@ -111,6 +113,7 @@ enum rq_flag_bits {
- };
- #define REQ_RW                (1 << __REQ_RW)
-+#define REQ_DISCARD   (1 << __REQ_DISCARD)
- #define REQ_FAILFAST  (1 << __REQ_FAILFAST)
- #define REQ_SORTED    (1 << __REQ_SORTED)
- #define REQ_SOFTBARRIER       (1 << __REQ_SOFTBARRIER)
-@@ -252,6 +255,7 @@ typedef void (request_fn_proc) (struct r
- typedef int (make_request_fn) (struct request_queue *q, struct bio *bio);
- typedef int (prep_rq_fn) (struct request_queue *, struct request *);
- typedef void (unplug_fn) (struct request_queue *);
-+typedef int (prepare_discard_fn) (struct request_queue *, struct request *);
- struct bio_vec;
- struct bvec_merge_data {
-@@ -307,6 +311,7 @@ struct request_queue
-       make_request_fn         *make_request_fn;
-       prep_rq_fn              *prep_rq_fn;
-       unplug_fn               *unplug_fn;
-+      prepare_discard_fn      *prepare_discard_fn;
-       merge_bvec_fn           *merge_bvec_fn;
-       prepare_flush_fn        *prepare_flush_fn;
-       softirq_done_fn         *softirq_done_fn;
-@@ -536,7 +541,7 @@ enum {
- #define blk_noretry_request(rq)       ((rq)->cmd_flags & REQ_FAILFAST)
- #define blk_rq_started(rq)    ((rq)->cmd_flags & REQ_STARTED)
--#define blk_account_rq(rq)    (blk_rq_started(rq) && blk_fs_request(rq))
-+#define blk_account_rq(rq)    (blk_rq_started(rq) && (blk_fs_request(rq) || blk_discard_rq(rq)))
- #define blk_pm_suspend_request(rq)    ((rq)->cmd_type == REQ_TYPE_PM_SUSPEND)
- #define blk_pm_resume_request(rq)     ((rq)->cmd_type == REQ_TYPE_PM_RESUME)
-@@ -546,6 +551,7 @@ enum {
- #define blk_sorted_rq(rq)     ((rq)->cmd_flags & REQ_SORTED)
- #define blk_barrier_rq(rq)    ((rq)->cmd_flags & REQ_HARDBARRIER)
- #define blk_fua_rq(rq)                ((rq)->cmd_flags & REQ_FUA)
-+#define blk_discard_rq(rq)    ((rq)->cmd_flags & REQ_DISCARD)
- #define blk_bidi_rq(rq)               ((rq)->next_rq != NULL)
- #define blk_empty_barrier(rq) (blk_barrier_rq(rq) && blk_fs_request(rq) && !(rq)->hard_nr_sectors)
- /* rq->queuelist of dequeued request must be list_empty() */
-@@ -592,7 +598,8 @@ static inline void blk_clear_queue_full(
- #define RQ_NOMERGE_FLAGS      \
-       (REQ_NOMERGE | REQ_STARTED | REQ_HARDBARRIER | REQ_SOFTBARRIER)
- #define rq_mergeable(rq)      \
--      (!((rq)->cmd_flags & RQ_NOMERGE_FLAGS) && blk_fs_request((rq)))
-+      (!((rq)->cmd_flags & RQ_NOMERGE_FLAGS) && \
-+       (blk_discard_rq(rq) || blk_fs_request((rq))))
- /*
-  * q->prep_rq_fn return values
-@@ -797,6 +804,7 @@ extern void blk_queue_merge_bvec(struct 
- extern void blk_queue_dma_alignment(struct request_queue *, int);
- extern void blk_queue_update_dma_alignment(struct request_queue *, int);
- extern void blk_queue_softirq_done(struct request_queue *, softirq_done_fn *);
-+extern void blk_queue_set_discard(struct request_queue *, prepare_discard_fn *);
- extern struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev);
- extern int blk_queue_ordered(struct request_queue *, unsigned, prepare_flush_fn *);
- extern int blk_do_ordered(struct request_queue *, struct request **);
-@@ -838,6 +846,16 @@ static inline struct request *blk_map_qu
- }
- extern int blkdev_issue_flush(struct block_device *, sector_t *);
-+extern int blkdev_issue_discard(struct block_device *, sector_t sector,
-+                              unsigned nr_sects);
-+
-+static inline int sb_issue_discard(struct super_block *sb,
-+                                 sector_t block, unsigned nr_blocks)
-+{
-+      block <<= (sb->s_blocksize_bits - 9);
-+      nr_blocks <<= (sb->s_blocksize_bits - 9);
-+      return blkdev_issue_discard(sb->s_bdev, block, nr_blocks);
-+}
- /*
- * command filter functions
---- a/include/linux/blktrace_api.h
-+++ b/include/linux/blktrace_api.h
-@@ -23,7 +23,8 @@ enum blktrace_cat {
-       BLK_TC_NOTIFY   = 1 << 10,      /* special message */
-       BLK_TC_AHEAD    = 1 << 11,      /* readahead */
-       BLK_TC_META     = 1 << 12,      /* metadata */
--      BLK_TC_DRV_DATA = 1 << 13,      /* binary per-driver data */
-+      BLK_TC_DISCARD  = 1 << 13,      /* discard requests */
-+      BLK_TC_DRV_DATA = 1 << 14,      /* binary per-drivers data */
-       BLK_TC_END      = 1 << 15,      /* only 16-bits, reminder */
- };
-@@ -204,6 +205,9 @@ static inline void blk_add_trace_rq(stru
-       if (likely(!bt))
-               return;
-+      if (blk_discard_rq(rq))
-+              rw |= (1 << BIO_RW_DISCARD);
-+
-       if (blk_pc_request(rq)) {
-               what |= BLK_TC_ACT(BLK_TC_PC);
-               __blk_add_trace(bt, 0, rq->data_len, rw, what, rq->errors, sizeof(rq->cmd), rq->cmd);
---- a/include/linux/fs.h
-+++ b/include/linux/fs.h
-@@ -91,7 +91,9 @@ extern int dir_notify_enable;
- #define READ_META     (READ | (1 << BIO_RW_META))
- #define WRITE_SYNC    (WRITE | (1 << BIO_RW_SYNC))
- #define SWRITE_SYNC   (SWRITE | (1 << BIO_RW_SYNC))
--#define WRITE_BARRIER ((1 << BIO_RW) | (1 << BIO_RW_BARRIER))
-+#define WRITE_BARRIER (WRITE | (1 << BIO_RW_BARRIER))
-+#define DISCARD_NOBARRIER (1 << BIO_RW_DISCARD)
-+#define DISCARD_BARRIER ((1 << BIO_RW_DISCARD) | (1 << BIO_RW_BARRIER))
- #define SEL_IN                1
- #define SEL_OUT               2
-@@ -229,6 +231,7 @@ extern int dir_notify_enable;
- #define BLKTRACESTART _IO(0x12,116)
- #define BLKTRACESTOP _IO(0x12,117)
- #define BLKTRACETEARDOWN _IO(0x12,118)
-+#define BLKDISCARD _IO(0x12,119)
- #define BMAP_IOCTL 1          /* obsolete - kept for compatibility */
- #define FIBMAP           _IO(0x00,1)  /* bmap access */
---- a/include/linux/mtd/blktrans.h
-+++ b/include/linux/mtd/blktrans.h
-@@ -41,6 +41,8 @@ struct mtd_blktrans_ops {
-                   unsigned long block, char *buffer);
-       int (*writesect)(struct mtd_blktrans_dev *dev,
-                    unsigned long block, char *buffer);
-+      int (*discard)(struct mtd_blktrans_dev *dev,
-+                     unsigned long block, unsigned nr_blocks);
-       /* Block layer ioctls */
-       int (*getgeo)(struct mtd_blktrans_dev *dev, struct hd_geometry *geo);