/* * Functions related to generic helpers functions */ #include #include #include #include #include #include "blk.h" 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); } if (bio->bi_private) complete(bio->bi_private); __free_page(bio_page(bio)); 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) * @flags: BLKDEV_IFL_* flags to control behaviour * * Description: * Issue a discard request for the sectors in question. */ int blkdev_issue_discard(struct block_device *bdev, sector_t sector, sector_t nr_sects, gfp_t gfp_mask, unsigned long flags) { DECLARE_COMPLETION_ONSTACK(wait); struct request_queue *q = bdev_get_queue(bdev); int type = flags & BLKDEV_IFL_BARRIER ? DISCARD_BARRIER : DISCARD_NOBARRIER; struct bio *bio; struct page *page; int ret = 0; if (!q) return -ENXIO; if (!blk_queue_discard(q)) return -EOPNOTSUPP; while (nr_sects && !ret) { unsigned int sector_size = q->limits.logical_block_size; unsigned int max_discard_sectors = min(q->limits.max_discard_sectors, UINT_MAX >> 9); bio = bio_alloc(gfp_mask, 1); if (!bio) goto out; bio->bi_sector = sector; bio->bi_end_io = blkdev_discard_end_io; bio->bi_bdev = bdev; if (flags & BLKDEV_IFL_WAIT) bio->bi_private = &wait; /* * Add a zeroed one-sector payload as that's what * our current implementations need. If we'll ever need * more the interface will need revisiting. */ page = alloc_page(gfp_mask | __GFP_ZERO); if (!page) goto out_free_bio; if (bio_add_pc_page(q, bio, page, sector_size, 0) < sector_size) goto out_free_page; /* * And override the bio size - the way discard works we * touch many more blocks on disk than the actual payload * length. */ if (nr_sects > max_discard_sectors) { bio->bi_size = max_discard_sectors << 9; nr_sects -= max_discard_sectors; sector += max_discard_sectors; } else { bio->bi_size = nr_sects << 9; nr_sects = 0; } bio_get(bio); submit_bio(type, bio); if (flags & BLKDEV_IFL_WAIT) 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; out_free_page: __free_page(page); out_free_bio: bio_put(bio); out: return -ENOMEM; } EXPORT_SYMBOL(blkdev_issue_discard);