From: willy@linux.intel.com (Matthew Wilcox)
Subject: [PATCH] NVMe: Add discard support for capable devices
Date: Tue, 6 Nov 2012 11:10:08 -0500 [thread overview]
Message-ID: <20121106161008.GA1736@linux.intel.com> (raw)
In-Reply-To: <1343933944-32726-1-git-send-email-keith.busch@intel.com>
On Thu, Aug 02, 2012@12:59:04PM -0600, Keith Busch wrote:
> This adds discard support to block queues if the nvme device is capable
> of deallocating blocks as indicated by the controller's optional command
> support. A discard flagged bio request will submit an NVMe deallocate
> Data Set Management command for the requested blocks.
Thanks for this.
> @@ -334,6 +335,7 @@ nvme_alloc_iod(unsigned nseg, unsigned nbytes, gfp_t gfp)
> iod->offset = offsetof(struct nvme_iod, sg[nseg]);
> iod->npages = -1;
> iod->length = nbytes;
> + iod->nents = 0;
> }
>
> return iod;
Are you initialising nents here to prevent having to do so in
nvme_submit_discard? I think we're relying on undefined behaviour of
dma_unmap_sg() in bio_completion() -- ie to be a no-op when nents is 0.
Probably we should check it explicitly; ie:
if (iod->nents)
dma_unmap_sg(...);
We're already relying on that behaviour for the case of submitting a
flush without data, so can you split this into two patches; one that
initialises iod->nents to 0 and checks it, then a second one that adds
the discard functionality?
> @@ -505,6 +507,39 @@ static int nvme_map_bio(struct device *dev, struct nvme_iod *iod,
> return length;
> }
>
> +static int nvme_submit_discard(struct nvme_queue *nvmeq, struct nvme_ns *ns,
> + struct bio *bio, struct nvme_iod *iod, int cmdid)
> +{
> + struct nvme_dsm_range *range;
> + struct nvme_command *cmnd = &nvmeq->sq_cmds[nvmeq->sq_tail];
Could we have a comment somewhere (possibly here) about how we're reusing
the prp small pool for this purpose instead of creating a special pool?
Maybe include the justification that it's not worth creating a new pool
to dole out these 16-byte ranges.
> + range = dma_pool_alloc(nvmeq->dev->prp_small_pool, GFP_ATOMIC,
> + &iod->first_dma);
> + if (range == NULL)
> + return -ENOMEM;
Usual style is
if (!range)
But ... we've got a problem here. If this allocation fails, we leak the
nvme_iod and the cmdid. We need to change the code below (skip down to
[A]):
> +
> + iod_list(iod)[0] = (__le64 *)range;
Brave. I like it :-)
> + iod->npages = 0;
> +
> + range->cattr = cpu_to_le32(0);
> + range->nlb = cpu_to_le32(bio->bi_size >> ns->lba_shift);
> + range->slba = cpu_to_le64(bio->bi_sector >> (ns->lba_shift - 9));
> +
> + memset(cmnd, 0, sizeof(*cmnd));
> + cmnd->dsm.opcode = nvme_cmd_dsm;
> + cmnd->dsm.command_id = cmdid;
> + cmnd->dsm.nsid = cpu_to_le32(ns->ns_id);
> + cmnd->dsm.prp1 = cpu_to_le64(iod->first_dma);
> + cmnd->dsm.nr = 0;
> + cmnd->dsm.attributes = cpu_to_le32(NVME_DSMGMT_AD);
> +
> + if (++nvmeq->sq_tail == nvmeq->q_depth)
> + nvmeq->sq_tail = 0;
> + writel(nvmeq->sq_tail, nvmeq->q_db);
> +
> + return 0;
> +}
> +
> static int nvme_submit_flush(struct nvme_queue *nvmeq, struct nvme_ns *ns,
> int cmdid)
> {
> @@ -562,6 +597,8 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns,
> if (unlikely(cmdid < 0))
> goto free_iod;
>
> + if (bio->bi_rw & REQ_DISCARD)
> + return nvme_submit_discard(nvmeq, ns, bio, iod, cmdid);
[A] If this fails, we need to do this:
+ if (bio->bi_rw & REQ_DISCARD) {
+ result = nvme_submit_discard(nvmeq, ns, bio, iod, cmdid);
+ if (result)
+ goto free_iod;
+ }
Although ... now I'm looking at the failure path in
nvme_submit_bio_queue(), isn't it the case that we'll leak cmdids if
nvme_map_bio() fails? And in this case? So I think we also need:
+ free_cmdid:
+ free_cmdid(nvmeq, cmdid, NULL);
free_iod:
And then change the 'goto free_iod' to 'goto free_cmdid'.
> if ((bio->bi_rw & REQ_FLUSH) && !psegs)
> return nvme_submit_flush(nvmeq, ns, cmdid);
>
> @@ -1316,6 +1353,19 @@ static void nvme_put_ns_idx(int index)
> spin_unlock(&dev_list_lock);
> }
>
> +static void nvme_config_discard(struct nvme_ns *ns)
> +{
> + u32 logical_block_size = queue_logical_block_size(ns->queue);
> + sector_t nr_sectors = get_capacity(ns->disk);
> +
> + ns->queue->limits.discard_zeroes_data = 0;
> + ns->queue->limits.discard_alignment = logical_block_size;
> + ns->queue->limits.discard_granularity = logical_block_size;
> + ns->queue->limits.max_discard_sectors = (u32)min_t(u64, nr_sectors,
> + 0xffffffff);
I don't think we need to set the limit to the maximum size of the block
device ... I don't see any other driver doing this. Just:
+ ns->queue->limits.max_discard_sectors = 0xffffffff;
should be fine (and delete the get_capacity() line).
next prev parent reply other threads:[~2012-11-06 16:10 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-08-02 18:59 [PATCH] NVMe: Add discard support for capable devices Keith Busch
2012-11-06 16:10 ` Matthew Wilcox [this message]
2012-11-06 16:15 ` Matthew Wilcox
2012-11-06 16:19 ` Busch, Keith
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20121106161008.GA1736@linux.intel.com \
--to=willy@linux.intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.