diff -u --recursive --new-file --exclude-from=linux-2.6.31-rc6/Documentation/dontdiff --exclude='*.lds' --exclude-from=linux-2.6.31-rc6/.gitignore linux-2.6.31-rc6/block/blk-barrier.c linux/block/blk-barrier.c --- linux-2.6.31-rc6/block/blk-barrier.c 2009-08-16 09:16:36.303766940 -0400 +++ linux/block/blk-barrier.c 2009-08-16 09:19:07.287086209 -0400 @@ -348,30 +348,22 @@ clear_bit(BIO_UPTODATE, &bio->bi_flags); } + if (bio_has_data(bio)) + __free_page(bio_page(bio)); + + if (bio->bi_private) + complete(bio->bi_private); + 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 - * @gfp_mask: memory allocation flags (for bio_alloc) - * - * Description: - * Issue a discard request for the sectors in question. Does not wait. - */ -int blkdev_issue_discard(struct block_device *bdev, - sector_t sector, sector_t nr_sects, gfp_t gfp_mask) +int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, + sector_t nr_sects, gfp_t gfp_mask, + unsigned type, struct completion *completion) { - struct request_queue *q; - struct bio *bio; int ret = 0; + struct request_queue *q = bdev_get_queue(bdev); - if (bdev->bd_disk == NULL) - return -ENXIO; - - q = bdev_get_queue(bdev); if (!q) return -ENXIO; @@ -379,12 +371,13 @@ return -EOPNOTSUPP; while (nr_sects && !ret) { - bio = bio_alloc(gfp_mask, 0); + struct bio *bio = bio_alloc(gfp_mask, 1); if (!bio) return -ENOMEM; bio->bi_end_io = blkdev_discard_end_io; bio->bi_bdev = bdev; + bio->bi_private = completion; bio->bi_sector = sector; @@ -396,10 +389,13 @@ bio->bi_size = nr_sects << 9; nr_sects = 0; } + bio_get(bio); - submit_bio(DISCARD_BARRIER, bio); + submit_bio(type, bio); + + if (completion) + wait_for_completion(completion); - /* Check if it failed immediately */ if (bio_flagged(bio, BIO_EOPNOTSUPP)) ret = -EOPNOTSUPP; else if (!bio_flagged(bio, BIO_UPTODATE)) @@ -408,4 +404,24 @@ } return ret; } + +/** + * blkdev_issue_discard - queue a discard + * @bdev: blockdev to issue discard for + * @sector: start sector + * @nr_sects: number of sectors to discard + * @gfp_mask: memory allocation flags (for bio_alloc) + * + * Description: + * Issue a discard request for the sectors in question. Does not wait. + */ +int blkdev_issue_discard(struct block_device *bdev, + sector_t sector, sector_t nr_sects, gfp_t gfp_mask) +{ + if (bdev->bd_disk == NULL) + return -ENXIO; + + return __blkdev_issue_discard(bdev, sector, nr_sects, gfp_mask, + DISCARD_BARRIER, NULL); +} EXPORT_SYMBOL(blkdev_issue_discard); diff -u --recursive --new-file --exclude-from=linux-2.6.31-rc6/Documentation/dontdiff --exclude='*.lds' --exclude-from=linux-2.6.31-rc6/.gitignore linux-2.6.31-rc6/block/blk-core.c linux/block/blk-core.c --- linux-2.6.31-rc6/block/blk-core.c 2009-08-16 09:16:36.307099905 -0400 +++ linux/block/blk-core.c 2009-08-16 08:53:19.000000000 -0400 @@ -1107,6 +1107,8 @@ void init_request_from_bio(struct request *req, struct bio *bio) { + might_sleep(); + req->cpu = bio->bi_comp_cpu; req->cmd_type = REQ_TYPE_FS; @@ -1127,7 +1129,7 @@ req->cmd_flags |= REQ_DISCARD; if (bio_barrier(bio)) req->cmd_flags |= REQ_SOFTBARRIER; - req->q->prepare_discard_fn(req->q, req); + req->q->prepare_discard_fn(req->q, req, bio); } else if (unlikely(bio_barrier(bio))) req->cmd_flags |= REQ_HARDBARRIER; diff -u --recursive --new-file --exclude-from=linux-2.6.31-rc6/Documentation/dontdiff --exclude='*.lds' --exclude-from=linux-2.6.31-rc6/.gitignore linux-2.6.31-rc6/block/blk.h linux/block/blk.h --- linux-2.6.31-rc6/block/blk.h 2009-08-16 09:16:36.310433289 -0400 +++ linux/block/blk.h 2009-08-16 08:53:19.000000000 -0400 @@ -17,6 +17,10 @@ struct bio *bio); void blk_dequeue_request(struct request *rq); void __blk_queue_free_tags(struct request_queue *q); +int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, + sector_t nr_sects, gfp_t gfp_mask, + unsigned type, struct completion *completion); + void blk_unplug_work(struct work_struct *work); void blk_unplug_timeout(unsigned long data); diff -u --recursive --new-file --exclude-from=linux-2.6.31-rc6/Documentation/dontdiff --exclude='*.lds' --exclude-from=linux-2.6.31-rc6/.gitignore linux-2.6.31-rc6/block/ioctl.c linux/block/ioctl.c --- linux-2.6.31-rc6/block/ioctl.c 2009-08-16 09:16:36.313766813 -0400 +++ linux/block/ioctl.c 2009-08-16 08:53:19.000000000 -0400 @@ -7,6 +7,7 @@ #include #include #include +#include "blk.h" static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user *arg) { @@ -112,21 +113,10 @@ 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; + DECLARE_COMPLETION_ONSTACK(wait); if (start & 511) return -EINVAL; @@ -138,39 +128,8 @@ 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); - - bio->bi_end_io = blk_ioc_discard_endio; - bio->bi_bdev = bdev; - bio->bi_private = &wait; - bio->bi_sector = start; - - if (len > queue_max_hw_sectors(q)) { - bio->bi_size = queue_max_hw_sectors(q) << 9; - len -= queue_max_hw_sectors(q); - start += queue_max_hw_sectors(q); - } 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; + return __blkdev_issue_discard(bdev, start, len, GFP_KERNEL, + DISCARD_NOBARRIER, &wait); } static int put_ushort(unsigned long arg, unsigned short val) diff -u --recursive --new-file --exclude-from=linux-2.6.31-rc6/Documentation/dontdiff --exclude='*.lds' --exclude-from=linux-2.6.31-rc6/.gitignore linux-2.6.31-rc6/drivers/ata/libata-scsi.c linux/drivers/ata/libata-scsi.c --- linux-2.6.31-rc6/drivers/ata/libata-scsi.c 2009-08-16 09:16:36.350433414 -0400 +++ linux/drivers/ata/libata-scsi.c 2009-08-16 08:53:19.000000000 -0400 @@ -1051,6 +1051,46 @@ desc[11] = block; } +static int ata_discard_fn(struct request_queue *q, struct request *req, + struct bio *bio) +{ + unsigned size; + struct page *page = alloc_page(GFP_KERNEL); + if (!page) + goto error; + + size = ata_set_lba_range_entries(page_address(page), PAGE_SIZE / 8, + bio->bi_sector, bio_sectors(bio)); + bio->bi_size = 0; + if (bio_add_pc_page(q, bio, page, size, 0) < size) + goto free_page; + + req->cmd_type = REQ_TYPE_BLOCK_PC; + req->cmd_len = 16; + req->cmd[0] = ATA_16; + req->cmd[1] = (6 << 1) | 1; /* dma, 48-bit */ + req->cmd[2] = 0x6; /* length, direction */ + req->cmd[3] = 0; /* feature high */ + req->cmd[4] = ATA_DSM_TRIM; /* feature low */ + req->cmd[5] = (size / 512) >> 8; /* nsect high */ + req->cmd[6] = size / 512; /* nsect low */ + req->cmd[7] = 0; /* lba */ + req->cmd[8] = 0; /* lba */ + req->cmd[9] = 0; /* lba */ + req->cmd[10] = 0; /* lba */ + req->cmd[11] = 0; /* lba */ + req->cmd[12] = 0; /* lba */ + req->cmd[13] = ATA_LBA; /* device */ + req->cmd[14] = ATA_CMD_DSM; /* command */ + req->cmd[15] = 0; /* control */ + + return 0; + free_page: + __free_page(page); + error: + return -ENOMEM; +} + static void ata_scsi_sdev_config(struct scsi_device *sdev) { sdev->use_10_for_rw = 1; @@ -1099,6 +1139,9 @@ /* configure max sectors */ blk_queue_max_sectors(sdev->request_queue, dev->max_sectors); + if (ata_id_has_trim(dev->id)) + blk_queue_set_discard(sdev->request_queue, ata_discard_fn); + if (dev->class == ATA_DEV_ATAPI) { struct request_queue *q = sdev->request_queue; void *buf; @@ -1747,6 +1790,12 @@ * whether the command completed successfully or not. If there * was no error, SK, ASC and ASCQ will all be zero. */ + + if (need_sense && qc->tf.command == ATA_CMD_DSM) { + ata_port_printk(ap, KERN_ERR, "%s: DISCARD/TRIM failed: disabling it\n", __func__); + blk_queue_set_discard(qc->dev->sdev->request_queue, NULL); + } + if (((cdb[0] == ATA_16) || (cdb[0] == ATA_12)) && ((cdb[2] & 0x20) || need_sense)) { ata_gen_passthru_sense(qc); diff -u --recursive --new-file --exclude-from=linux-2.6.31-rc6/Documentation/dontdiff --exclude='*.lds' --exclude-from=linux-2.6.31-rc6/.gitignore linux-2.6.31-rc6/drivers/mtd/mtd_blkdevs.c linux/drivers/mtd/mtd_blkdevs.c --- linux-2.6.31-rc6/drivers/mtd/mtd_blkdevs.c 2009-08-16 09:16:36.963766818 -0400 +++ linux/drivers/mtd/mtd_blkdevs.c 2009-08-16 08:53:19.000000000 -0400 @@ -33,7 +33,7 @@ }; static int blktrans_discard_request(struct request_queue *q, - struct request *req) + struct request *req, struct bio *bio) { req->cmd_type = REQ_TYPE_LINUX_BLOCK; req->cmd[0] = REQ_LB_OP_DISCARD; diff -u --recursive --new-file --exclude-from=linux-2.6.31-rc6/Documentation/dontdiff --exclude='*.lds' --exclude-from=linux-2.6.31-rc6/.gitignore linux-2.6.31-rc6/include/linux/blkdev.h linux/include/linux/blkdev.h --- linux-2.6.31-rc6/include/linux/blkdev.h 2009-08-16 09:16:39.053766322 -0400 +++ linux/include/linux/blkdev.h 2009-08-16 08:53:19.000000000 -0400 @@ -255,7 +255,8 @@ 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 *); +typedef int (prepare_discard_fn) (struct request_queue *, struct request *, + struct bio *bio); struct bio_vec; struct bvec_merge_data { diff -u --recursive --new-file --exclude-from=linux-2.6.31-rc6/Documentation/dontdiff --exclude='*.lds' --exclude-from=linux-2.6.31-rc6/.gitignore linux-2.6.31-rc6/include/linux/fs.h linux/include/linux/fs.h --- linux-2.6.31-rc6/include/linux/fs.h 2009-08-16 09:16:39.070433246 -0400 +++ linux/include/linux/fs.h 2009-08-16 08:53:19.000000000 -0400 @@ -161,8 +161,8 @@ * These aren't really reads or writes, they pass down information about * parts of device that are now unused by the file system. */ -#define DISCARD_NOBARRIER (1 << BIO_RW_DISCARD) -#define DISCARD_BARRIER ((1 << BIO_RW_DISCARD) | (1 << BIO_RW_BARRIER)) +#define DISCARD_NOBARRIER (WRITE | (1 << BIO_RW_DISCARD)) +#define DISCARD_BARRIER (DISCARD_NOBARRIER | (1 << BIO_RW_BARRIER)) #define SEL_IN 1 #define SEL_OUT 2