Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

nbd: fix partial sending

nbd driver sends request header and payload with multiple call of
sock_sendmsg, and partial sending can't be avoided. However, nbd driver
returns BLK_STS_RESOURCE to block core in this situation. This way causes
one issue: request->tag may change in the next run of nbd_queue_rq(), but
the original old tag has been sent as part of header cookie, this way
confuses nbd driver reply handling, since the real request can't be
retrieved any more with the obsolete old tag.

Fix it by retrying sending directly in per-socket work function,
meantime return BLK_STS_OK to block layer core.

Cc: vincent.chen@sifive.com
Cc: Leon Schuermann <leon@is.currently.online>
Cc: Bart Van Assche <bvanassche@acm.org>
Reported-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Ming Lei <ming.lei@redhat.com>
Tested-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Link: https://lore.kernel.org/r/20241029011941.153037-1-ming.lei@redhat.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Ming Lei and committed by
Jens Axboe
8337b029 7c0be4ea

+85 -10
+85 -10
drivers/block/nbd.c
··· 62 62 bool dead; 63 63 int fallback_index; 64 64 int cookie; 65 + struct work_struct work; 65 66 }; 66 67 67 68 struct recv_thread_args { ··· 141 140 * by cmd->lock. 142 141 */ 143 142 #define NBD_CMD_INFLIGHT 2 143 + 144 + /* Just part of request header or data payload is sent successfully */ 145 + #define NBD_CMD_PARTIAL_SEND 3 144 146 145 147 struct nbd_cmd { 146 148 struct nbd_device *nbd; ··· 457 453 if (!mutex_trylock(&cmd->lock)) 458 454 return BLK_EH_RESET_TIMER; 459 455 456 + /* partial send is handled in nbd_sock's work function */ 457 + if (test_bit(NBD_CMD_PARTIAL_SEND, &cmd->flags)) { 458 + mutex_unlock(&cmd->lock); 459 + return BLK_EH_RESET_TIMER; 460 + } 461 + 460 462 if (!test_bit(NBD_CMD_INFLIGHT, &cmd->flags)) { 461 463 mutex_unlock(&cmd->lock); 462 464 return BLK_EH_DONE; ··· 612 602 } 613 603 614 604 /* 605 + * We've already sent header or part of data payload, have no choice but 606 + * to set pending and schedule it in work. 607 + * 608 + * And we have to return BLK_STS_OK to block core, otherwise this same 609 + * request may be re-dispatched with different tag, but our header has 610 + * been sent out with old tag, and this way does confuse reply handling. 611 + */ 612 + static void nbd_sched_pending_work(struct nbd_device *nbd, 613 + struct nbd_sock *nsock, 614 + struct nbd_cmd *cmd, int sent) 615 + { 616 + struct request *req = blk_mq_rq_from_pdu(cmd); 617 + 618 + /* pending work should be scheduled only once */ 619 + WARN_ON_ONCE(test_bit(NBD_CMD_PARTIAL_SEND, &cmd->flags)); 620 + 621 + nsock->pending = req; 622 + nsock->sent = sent; 623 + set_bit(NBD_CMD_PARTIAL_SEND, &cmd->flags); 624 + refcount_inc(&nbd->config_refs); 625 + schedule_work(&nsock->work); 626 + } 627 + 628 + /* 615 629 * Returns BLK_STS_RESOURCE if the caller should retry after a delay. 616 630 * Returns BLK_STS_IOERR if sending failed. 617 631 */ ··· 720 686 * completely done. 721 687 */ 722 688 if (sent) { 723 - nsock->pending = req; 724 - nsock->sent = sent; 689 + nbd_sched_pending_work(nbd, nsock, cmd, sent); 690 + return BLK_STS_OK; 725 691 } 726 692 set_bit(NBD_CMD_REQUEUED, &cmd->flags); 727 693 return BLK_STS_RESOURCE; ··· 758 724 result = sock_xmit(nbd, index, 1, &from, flags, &sent); 759 725 if (result < 0) { 760 726 if (was_interrupted(result)) { 761 - /* We've already sent the header, we 762 - * have no choice but to set pending and 763 - * return BUSY. 764 - */ 765 - nsock->pending = req; 766 - nsock->sent = sent; 767 - set_bit(NBD_CMD_REQUEUED, &cmd->flags); 768 - return BLK_STS_RESOURCE; 727 + nbd_sched_pending_work(nbd, nsock, cmd, sent); 728 + return BLK_STS_OK; 769 729 } 770 730 dev_err(disk_to_dev(nbd->disk), 771 731 "Send data failed (result %d)\n", ··· 785 757 return BLK_STS_OK; 786 758 787 759 requeue: 760 + /* 761 + * Can't requeue in case we are dealing with partial send 762 + * 763 + * We must run from pending work function. 764 + * */ 765 + if (test_bit(NBD_CMD_PARTIAL_SEND, &cmd->flags)) 766 + return BLK_STS_OK; 767 + 788 768 /* retry on a different socket */ 789 769 dev_err_ratelimited(disk_to_dev(nbd->disk), 790 770 "Request send failed, requeueing\n"); 791 771 nbd_mark_nsock_dead(nbd, nsock, 1); 792 772 nbd_requeue_cmd(cmd); 793 773 return BLK_STS_OK; 774 + } 775 + 776 + /* handle partial sending */ 777 + static void nbd_pending_cmd_work(struct work_struct *work) 778 + { 779 + struct nbd_sock *nsock = container_of(work, struct nbd_sock, work); 780 + struct request *req = nsock->pending; 781 + struct nbd_cmd *cmd = blk_mq_rq_to_pdu(req); 782 + struct nbd_device *nbd = cmd->nbd; 783 + unsigned long deadline = READ_ONCE(req->deadline); 784 + unsigned int wait_ms = 2; 785 + 786 + mutex_lock(&cmd->lock); 787 + 788 + WARN_ON_ONCE(test_bit(NBD_CMD_REQUEUED, &cmd->flags)); 789 + if (WARN_ON_ONCE(!test_bit(NBD_CMD_PARTIAL_SEND, &cmd->flags))) 790 + goto out; 791 + 792 + mutex_lock(&nsock->tx_lock); 793 + while (true) { 794 + nbd_send_cmd(nbd, cmd, cmd->index); 795 + if (!nsock->pending) 796 + break; 797 + 798 + /* don't bother timeout handler for partial sending */ 799 + if (READ_ONCE(jiffies) + msecs_to_jiffies(wait_ms) >= deadline) { 800 + cmd->status = BLK_STS_IOERR; 801 + blk_mq_complete_request(req); 802 + break; 803 + } 804 + msleep(wait_ms); 805 + wait_ms *= 2; 806 + } 807 + mutex_unlock(&nsock->tx_lock); 808 + clear_bit(NBD_CMD_PARTIAL_SEND, &cmd->flags); 809 + out: 810 + mutex_unlock(&cmd->lock); 811 + nbd_config_put(nbd); 794 812 } 795 813 796 814 static int nbd_read_reply(struct nbd_device *nbd, struct socket *sock, ··· 1285 1211 nsock->pending = NULL; 1286 1212 nsock->sent = 0; 1287 1213 nsock->cookie = 0; 1214 + INIT_WORK(&nsock->work, nbd_pending_cmd_work); 1288 1215 socks[config->num_connections++] = nsock; 1289 1216 atomic_inc(&config->live_connections); 1290 1217 blk_mq_unfreeze_queue(nbd->disk->queue);