Split queue lock into submission/completion locks
Jens Axboe
maintainercover Split queue lock into submission/completion locks
This series is on top of the previous work from yesterday, including Christophs patch. It splits nvmeq->q_lock into two locks:
1) ->sq_lock, that protects the submission side of things
2) ->cq_lock, that protects the completion side
This also means that we can drop the IRQ safe nature of the submission side locking.
drivers/nvme/host/pci.c | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-)
Jens Axboe
maintainerPATCH 1/3 nvme: remove ->cq_vector == -1 check in nvme_queue_rq()
We only clear this after calling nvme_suspend_queue(), which must have called nvme_stop_queues() first. The latter ensures that no more IO is queued, or in progress of being queued, against this hardware queue.
--- drivers/nvme/host/pci.c | 5 ----- 1 file changed, 5 deletions(-)
drivers/nvme/host/pci.c +0 -5
@@ -887,11 +887,6 @@ static blk_status_t nvme_queue_rq(struct blk_mq_hw_ctx *hctx,blk_mq_start_request (req);spin_lock_irq (&nvmeq->q_lock); -if (unlikely (nvmeq->cq_vector <0 )) { - ret =BLK_STS_IOERR ; -spin_unlock_irq (&nvmeq->q_lock); - goto out_cleanup_iod; - }__nvme_submit_cmd (nvmeq, &cmnd);spin_unlock_irq (&nvmeq->q_lock);return BLK_STS_OK ;
Signature · 1 line
2.7.4
PATCH 1/3 nvme: remove ->cq_vector == -1 check in nvme_queue_rq()
Looks good. I somehow remember seeing an equivalent patch on the list a while ago, though..
PATCH 1/3 nvme: remove ->cq_vector == -1 check in nvme_queue_rq()
In reply to Jens AxboeQuoted 16 lines · Jens Axboe
We only clear this after calling
nvme_suspend_queue(), which must have callednvme_stop_queues()first. The latter ensures that no more IO is queued, or in progress of being queued, against this hardware queue.Signed-off-by Jens Axboe <axboe at kernel.dk>--- drivers/nvme/host/pci.c | 5 ----- 1 file changed, 5 deletions(-)drivers/nvme/host/pci.c +0
-0@@ -887,11 +887,6 @@ static blk_status_t nvme_queue_rq(struct blk_mq_hw_ctx *hctx,blk_mq_start_request (req);
drivers/nvme/host/pci.c static blk_status_t nvme_queue_rq(struct blk_mq_hw_ctx *hctx, · line 892
Quoted 10 lines
drivers/nvme/host/pci.c +0
-5spin_lock_irq (&nvmeq->q_lock); -if (unlikely (nvmeq->cq_vector <0 )) { - ret =BLK_STS_IOERR ; -spin_unlock_irq (&nvmeq->q_lock); - goto out_cleanup_iod; - }__nvme_submit_cmd (nvmeq, &cmnd);spin_unlock_irq (&nvmeq->q_lock);return BLK_STS_OK ;
Unfortunatley we are still relying on this to drain entered requests on a dying queue: we restart them to flush to out requests to complete with error. :(
There's probably a better way to handle this.
Jens Axboe
maintainerPATCH 1/3 nvme: remove ->cq_vector == -1 check in nvme_queue_rq()
Quoted 32 lines · Keith Busch
Quoted 16 lines · Jens Axboe
We only clear this after calling
nvme_suspend_queue(), which must have callednvme_stop_queues()first. The latter ensures that no more IO is queued, or in progress of being queued, against this hardware queue.Signed-off-by Jens Axboe <axboe at kernel.dk>--- drivers/nvme/host/pci.c | 5 ----- 1 file changed, 5 deletions(-)drivers/nvme/host/pci.c +0
-0@@ -887,11 +887,6 @@ static blk_status_t nvme_queue_rq(struct blk_mq_hw_ctx *hctx,blk_mq_start_request (req);drivers/nvme/host/pci.c static blk_status_t nvme_queue_rq(struct blk_mq_hw_ctx *hctx, · line 892
Quoted 10 lines
drivers/nvme/host/pci.c +0
-5spin_lock_irq (&nvmeq->q_lock); -if (unlikely (nvmeq->cq_vector <0 )) { - ret =BLK_STS_IOERR ; -spin_unlock_irq (&nvmeq->q_lock); - goto out_cleanup_iod; - }__nvme_submit_cmd (nvmeq, &cmnd);spin_unlock_irq (&nvmeq->q_lock);return BLK_STS_OK ;Unfortunatley we are still relying on this to drain entered requests on a dying queue: we restart them to flush to out requests to complete with error. :(
Quoted 1 line
There's probably a better way to handle this.
I'd suggest we just move it to the top and get it out of the way instead, and ensure that the ->cq_vector to -1 includes an mb(). Then we can just make it:
if (unlikely(nvmeq->cq_vector < 0))
return BLK_STS_IOERR;
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH 1/3 nvme: remove ->cq_vector == -1 check in nvme_queue_rq()
Quoted 33 lines · Jens Axboe
Quoted 32 lines · Keith Busch
Quoted 16 lines · Jens Axboe
We only clear this after calling
nvme_suspend_queue(), which must have callednvme_stop_queues()first. The latter ensures that no more IO is queued, or in progress of being queued, against this hardware queue.Signed-off-by Jens Axboe <axboe at kernel.dk>--- drivers/nvme/host/pci.c | 5 ----- 1 file changed, 5 deletions(-)drivers/nvme/host/pci.c +0
-0@@ -887,11 +887,6 @@ static blk_status_t nvme_queue_rq(struct blk_mq_hw_ctx *hctx,blk_mq_start_request (req);drivers/nvme/host/pci.c static blk_status_t nvme_queue_rq(struct blk_mq_hw_ctx *hctx, · line 892
Quoted 10 lines
drivers/nvme/host/pci.c +0
-5spin_lock_irq (&nvmeq->q_lock); -if (unlikely (nvmeq->cq_vector <0 )) { - ret =BLK_STS_IOERR ; -spin_unlock_irq (&nvmeq->q_lock); - goto out_cleanup_iod; - }__nvme_submit_cmd (nvmeq, &cmnd);spin_unlock_irq (&nvmeq->q_lock);return BLK_STS_OK ;Unfortunatley we are still relying on this to drain entered requests on a dying queue: we restart them to flush to out requests to complete with error. :(
Quoted 8 lines
Quoted 1 line
There's probably a better way to handle this.
I'd suggest we just move it to the top and get it out of the way instead, and ensure that the ->cq_vector to -1 includes an
mb(). Then we can just make it:if (
unlikely(nvmeq->cq_vector < 0))return BLK_STS_IOERR;
How about this:
Signature · 1 line
Jens Axboe
PATCH 1/3 nvme: remove ->cq_vector == -1 check in nvme_queue_rq()
Quoted 3 lines · Jens Axboe
How about this:
Not only better, this also fixes a memory leak: we never called the dma_unmap_sg() on this error out case! Let's consider this patch separately.
Jens Axboe
maintainerPATCH 1/3 nvme: remove ->cq_vector == -1 check in nvme_queue_rq()
Quoted 8 lines · Keith Busch
Quoted 3 lines · Jens Axboe
How about this:
Not only better, this also fixes a memory leak: we never called the
dma_unmap_sg()on this error out case! Let's consider this patch separately.
Heh, didn't even notice that.
Christoph, let me know if you want me to post the whole series for your reshuffling. It's what's on:
http://git.kernel.dk/cgit/linux-block/log/?h=nvme-4.18
now.
Signature · 1 line
Jens Axboe
PATCH 1/3 nvme: remove ->cq_vector == -1 check in nvme_queue_rq()
Quoted 4 lines · Jens Axboe
Christoph, let me know if you want me to post the whole series for your reshuffling. It's what's on:
I'll cherry pick that in.
PATCH 1/3 nvme: remove ->cq_vector == -1 check in nvme_queue_rq()
In reply to Jens AxboeQuoted 6 lines · Jens Axboe
I'd suggest we just move it to the top and get it out of the way instead, and ensure that the ->cq_vector to -1 includes an
mb(). Then we can just make it:if (
unlikely(nvmeq->cq_vector < 0))return BLK_STS_IOERR;
Yes, I like that!
Jens Axboe
maintainerPATCH 2/3 nvme: split the nvme queue lock into submission and completion locks
In reply to Jens AxboeThis is now feasible. We protect the submission queue ring with ->sq_lock, and the completion side with ->cq_lock.
--- drivers/nvme/host/pci.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-)
drivers/nvme/host/pci.c +19 -17
@@ -146,9 +146,10 @@ static inline struct nvme_dev *to_nvme_dev(struct nvme_ctrl *ctrl)struct nvme_queue {struct device *q_dmadev;struct nvme_dev *dev; -spinlock_t q_lock; +spinlock_t sq_lock;struct nvme_command *sq_cmds;struct nvme_command __iomem *sq_cmds_io; +spinlock_t cq_lock____cacheline_aligned_in_smp ;volatile struct nvme_completion *cqes;struct blk_mq_tags **tags;dma_addr_t sq_dma_addr;
@@ -886,9 +887,9 @@ static blk_status_t nvme_queue_rq(struct blk_mq_hw_ctx *hctx,blk_mq_start_request (req); -spin_lock_irq (&nvmeq->q_lock); +spin_lock_irq (&nvmeq->sq_lock);__nvme_submit_cmd (nvmeq, &cmnd); -spin_unlock_irq (&nvmeq->q_lock); +spin_unlock_irq (&nvmeq->sq_lock);return BLK_STS_OK ; out_cleanup_iod:nvme_free_iod (dev, req);
@@ -992,9 +993,9 @@ static irqreturn_t nvme_irq(int irq, void *data)struct nvme_queue *nvmeq = data;u16 start, end; -spin_lock (&nvmeq->q_lock); +spin_lock (&nvmeq->cq_lock);nvme_process_cq (nvmeq, &start, &end); -spin_unlock (&nvmeq->q_lock); +spin_unlock (&nvmeq->cq_lock);return nvme_complete_cqes (nvmeq, start, end); }
@@ -1014,9 +1015,9 @@ static int __nvme_poll(struct nvme_queue *nvmeq, unsigned int tag)if (!nvme_cqe_valid (nvmeq, nvmeq->cq_head, nvmeq->cq_phase))return 0 ; -spin_lock_irq (&nvmeq->q_lock); +spin_lock_irq (&nvmeq->cq_lock);nvme_process_cq (nvmeq, &start, &end); -spin_unlock_irq (&nvmeq->q_lock); +spin_unlock_irq (&nvmeq->cq_lock);while (start != end) {if (nvme_handle_cqe (nvmeq, start, tag))
@@ -1045,9 +1046,9 @@ static void nvme_pci_submit_async_event(struct nvme_ctrl *ctrl) c.common.opcode = nvme_admin_async_event; c.common.command_id =NVME_AQ_BLK_MQ_DEPTH ; -spin_lock_irq (&nvmeq->q_lock); +spin_lock_irq (&nvmeq->sq_lock);__nvme_submit_cmd (nvmeq, &c); -spin_unlock_irq (&nvmeq->q_lock); +spin_unlock_irq (&nvmeq->sq_lock); }static int adapter_delete_queue (struct nvme_dev *dev,u8 opcode,u16 id)
@@ -1313,15 +1314,15 @@ static int nvme_suspend_queue(struct nvme_queue *nvmeq) {int vector; -spin_lock_irq (&nvmeq->q_lock); +spin_lock_irq (&nvmeq->cq_lock);if (nvmeq->cq_vector ==-1 ) { -spin_unlock_irq (&nvmeq->q_lock); +spin_unlock_irq (&nvmeq->cq_lock);return 1 ; } vector = nvmeq->cq_vector; nvmeq->dev->online_queues--; nvmeq->cq_vector =-1 ; -spin_unlock_irq (&nvmeq->q_lock); +spin_unlock_irq (&nvmeq->cq_lock);if (!nvmeq->qid && nvmeq->dev->ctrl.admin_q)blk_mq_quiesce_queue (nvmeq->dev->ctrl.admin_q);
@@ -1398,7 +1399,8 @@ static int nvme_alloc_queue(struct nvme_dev *dev, int qid, int depth) nvmeq->q_dmadev = dev->dev; nvmeq->dev = dev; -spin_lock_init (&nvmeq->q_lock); +spin_lock_init (&nvmeq->sq_lock); +spin_lock_init (&nvmeq->cq_lock); nvmeq->cq_head =0 ; nvmeq->cq_phase =1 ; nvmeq->q_db = &dev->dbs[qid *2 * dev->db_stride];
@@ -1434,7 +1436,7 @@ static void nvme_init_queue(struct nvme_queue *nvmeq, u16 qid) {struct nvme_dev *dev = nvmeq->dev; -spin_lock_irq (&nvmeq->q_lock); +spin_lock_irq (&nvmeq->cq_lock); nvmeq->sq_tail =0 ; nvmeq->cq_head =0 ; nvmeq->cq_phase =1 ;
@@ -1442,7 +1444,7 @@ static void nvme_init_queue(struct nvme_queue *nvmeq, u16 qid)memset ((void *)nvmeq->cqes,0 ,CQ_SIZE (nvmeq->q_depth));nvme_dbbuf_init (dev, nvmeq, qid); dev->online_queues++; -spin_unlock_irq (&nvmeq->q_lock); +spin_unlock_irq (&nvmeq->cq_lock); }static int nvme_create_queue (struct nvme_queue *nvmeq,int qid)
@@ -1997,10 +1999,10 @@ static void nvme_del_cq_end(struct request *req, blk_status_t error)* and the I/O queue q_lock should always * nest inside the AQ one. */ -spin_lock_irqsave_nested (&nvmeq->q_lock, flags, +spin_lock_irqsave_nested (&nvmeq->cq_lock, flags,SINGLE_DEPTH_NESTING );nvme_process_cq (nvmeq, &start, &end); -spin_unlock_irqrestore (&nvmeq->q_lock, flags); +spin_unlock_irqrestore (&nvmeq->cq_lock, flags);nvme_complete_cqes (nvmeq, start, end); }
Signature · 1 line
2.7.4
PATCH 2/3 nvme: split the nvme queue lock into submission and completion locks
drivers/nvme/host/pci.c static blk_status_t nvme_queue_rq(struct blk_mq_hw_ctx *hctx, · line 892
Quoted 5 lines
drivers/nvme/host/pci.c +2
-2-spin_lock_irq (&nvmeq->q_lock); +spin_lock_irq (&nvmeq->sq_lock);__nvme_submit_cmd (nvmeq, &cmnd); -spin_unlock_irq (&nvmeq->q_lock); +spin_unlock_irq (&nvmeq->sq_lock);
I don't think we need to disable irqs here anymore.
Jens Axboe
maintainerPATCH 2/3 nvme: split the nvme queue lock into submission and completion locks
Quoted 7 lines · Christoph Hellwig
drivers/nvme/host/pci.c static blk_status_t nvme_queue_rq(struct blk_mq_hw_ctx *hctx, · line 892
Quoted 5 lines
drivers/nvme/host/pci.c +2
-2-spin_lock_irq (&nvmeq->q_lock); +spin_lock_irq (&nvmeq->sq_lock);__nvme_submit_cmd (nvmeq, &cmnd); -spin_unlock_irq (&nvmeq->q_lock); +spin_unlock_irq (&nvmeq->sq_lock);I don't think we need to disable irqs here anymore.
See patch #3 :-)
I didn't want to do that in the same order.
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH 3/3 nvme: drop IRQ disabling on submission queue lock
In reply to Jens AxboeSince we aren't sharing the lock for completions now, we don't have to make it IRQ safe.
--- drivers/nvme/host/pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/nvme/host/pci.c +4 -4
@@ -887,9 +887,9 @@ static blk_status_t nvme_queue_rq(struct blk_mq_hw_ctx *hctx,blk_mq_start_request (req); -spin_lock_irq (&nvmeq->sq_lock); +spin_lock (&nvmeq->sq_lock);__nvme_submit_cmd (nvmeq, &cmnd); -spin_unlock_irq (&nvmeq->sq_lock); +spin_unlock (&nvmeq->sq_lock);return BLK_STS_OK ; out_cleanup_iod:nvme_free_iod (dev, req);
@@ -1046,9 +1046,9 @@ static void nvme_pci_submit_async_event(struct nvme_ctrl *ctrl) c.common.opcode = nvme_admin_async_event; c.common.command_id =NVME_AQ_BLK_MQ_DEPTH ; -spin_lock_irq (&nvmeq->sq_lock); +spin_lock (&nvmeq->sq_lock);__nvme_submit_cmd (nvmeq, &c); -spin_unlock_irq (&nvmeq->sq_lock); +spin_unlock (&nvmeq->sq_lock); }static int adapter_delete_queue (struct nvme_dev *dev,u8 opcode,u16 id)
Signature · 1 line
2.7.4
PATCH 3/3 nvme: drop IRQ disabling on submission queue lock
Quoted 2 lines · Jens Axboe
Since we aren't sharing the lock for completions now, we don't have to make it IRQ safe.
Ah, you did this as a separate patch..
for both.
Jens Axboe
maintainercover Split queue lock into submission/completion locks
In reply to Jens AxboeQuoted 8 lines · Jens Axboe
This series is on top of the previous work from yesterday, including Christophs patch. It splits
nvmeq->q_lockinto two locks:1) ->sq_lock, that protects the submission side of things
2) ->cq_lock, that protects the completion sideThis also means that we can drop the IRQ safe nature of the submission side locking.
Series also here:
http://git.kernel.dk/cgit/linux-block/log/?h=nvme-4.18
NOT on top of Christophs change, as it seems to regress performance for me.
Quick test on polling on an xpoint device with 4 threads shows a nice improvement for me:
Current master:
read: IOPS=519k, BW=253MiB/s (266MB/s)(2532MiB/10001msec)
clat (usec): min=5, max=630, avg= 7.38, stdev= 3.16
lat (usec): min=5, max=630, avg= 7.41, stdev= 3.16
clat percentiles (nsec):
| 1.00th=[ 6560], 5.00th=[ 6688], 10.00th=[ 6688], 20.00th=[ 6752],
| 30.00th=[ 6816], 40.00th=[ 6880], 50.00th=[ 6944], 60.00th=[ 7072],
| 70.00th=[ 7136], 80.00th=[ 7328], 90.00th=[ 7712], 95.00th=[ 8256],
| 99.00th=[18048], 99.50th=[19072], 99.90th=[36096], 99.95th=[36608],
| 99.99th=[41216]and with this series:
read: IOPS=507k, BW=248MiB/s (260MB/s)(2476MiB/10001msec)
clat (usec): min=6, max=640, avg= 7.56, stdev= 3.19
lat (usec): min=6, max=640, avg= 7.59, stdev= 3.19
clat percentiles (nsec):
| 1.00th=[ 6688], 5.00th=[ 6816], 10.00th=[ 6880], 20.00th=[ 6944],
| 30.00th=[ 7008], 40.00th=[ 7072], 50.00th=[ 7136], 60.00th=[ 7200],
| 70.00th=[ 7328], 80.00th=[ 7520], 90.00th=[ 7904], 95.00th=[ 8384],
| 99.00th=[18048], 99.50th=[18816], 99.90th=[36096], 99.95th=[36608],
| 99.99th=[42752]For polling, looking at CPU utilization usually means seeing if we decrease system time and shift that to app time instead. For master, it's 5.49/94.52% usr/sys, and with the patchset it's 5.71/94.27%.
Signature · 1 line
Jens Axboe