nvme-tcp: fix H2CData PDU send accounting (again)
We should not access request members after the last send, even to determine if indeed it was the last data payload send. The reason is that a completion could have arrived and trigger a new execution of the request which overridden these members. This was fixed by commit825619b09a
("nvme-tcp: fix possible use-after-completion"). Commite371af033c
broke that assumption again to address cases where multiple r2t pdus are sent per request. To fix it, we need to record the request data_sent and data_len and after the payload network send we reference these counters to determine weather we should advance the request iterator. Fixes:e371af033c
("nvme-tcp: fix incorrect h2cdata pdu offset accounting") Reported-by: Keith Busch <kbusch@kernel.org> Cc: stable@vger.kernel.org # 5.10+ Signed-off-by: Sagi Grimberg <sagi@grimberg.me> Reviewed-by: Keith Busch <kbusch@kernel.org> Signed-off-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
parent
926245c7d2
commit
25e1f67eda
@ -926,12 +926,14 @@ static void nvme_tcp_fail_request(struct nvme_tcp_request *req)
|
|||||||
static int nvme_tcp_try_send_data(struct nvme_tcp_request *req)
|
static int nvme_tcp_try_send_data(struct nvme_tcp_request *req)
|
||||||
{
|
{
|
||||||
struct nvme_tcp_queue *queue = req->queue;
|
struct nvme_tcp_queue *queue = req->queue;
|
||||||
|
int req_data_len = req->data_len;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
struct page *page = nvme_tcp_req_cur_page(req);
|
struct page *page = nvme_tcp_req_cur_page(req);
|
||||||
size_t offset = nvme_tcp_req_cur_offset(req);
|
size_t offset = nvme_tcp_req_cur_offset(req);
|
||||||
size_t len = nvme_tcp_req_cur_length(req);
|
size_t len = nvme_tcp_req_cur_length(req);
|
||||||
bool last = nvme_tcp_pdu_last_send(req, len);
|
bool last = nvme_tcp_pdu_last_send(req, len);
|
||||||
|
int req_data_sent = req->data_sent;
|
||||||
int ret, flags = MSG_DONTWAIT;
|
int ret, flags = MSG_DONTWAIT;
|
||||||
|
|
||||||
if (last && !queue->data_digest && !nvme_tcp_queue_more(queue))
|
if (last && !queue->data_digest && !nvme_tcp_queue_more(queue))
|
||||||
@ -958,7 +960,7 @@ static int nvme_tcp_try_send_data(struct nvme_tcp_request *req)
|
|||||||
* in the request where we don't want to modify it as we may
|
* in the request where we don't want to modify it as we may
|
||||||
* compete with the RX path completing the request.
|
* compete with the RX path completing the request.
|
||||||
*/
|
*/
|
||||||
if (req->data_sent + ret < req->data_len)
|
if (req_data_sent + ret < req_data_len)
|
||||||
nvme_tcp_advance_req(req, ret);
|
nvme_tcp_advance_req(req, ret);
|
||||||
|
|
||||||
/* fully successful last send in current PDU */
|
/* fully successful last send in current PDU */
|
||||||
|
Loading…
Reference in New Issue
Block a user