[PATCH] nvmet-tcp: fix a hang on queue teardown with data digest

Keith Busch kbusch at kernel.org
Thu Sep 10 15:32:31 PDT 2026


On Mon, Aug 31, 2026 at 09:47:18PM -0400, Shivam Kumar wrote:
> With data digest on, a command that has received all its data waits for
> the digest in NVMET_TCP_RECV_DDGST. has_data_in() is already false there,
> so need_data_in() is too, and nvmet_tcp_uninit_data_in_cmds() skips it on
> teardown even though it still holds the nvmet_req_init() reference. The SQ
> percpu_ref never drains, nvmet_sq_destroy() blocks forever in
> wait_for_completion(), and the nvmet-wq release worker is stuck.
> 
> An unauthenticated host on an allow_any_host subsystem hits this by
> negotiating data digest, sending a write's data but not the trailing
> digest, and closing the connection. Each leaked command wedges a release
> worker; a few stall queue teardown entirely, and with hung_task_panic the
> box goes down.
> 
> Drop the reference for a command left in RECV_DDGST from
> nvmet_tcp_release_queue_work(), before rcv_state is cleared. A command
> that failed nvmet_req_init() never took one, so skip it.

"I have made this letter longer than usual because I lack the time to
make it shorter." - Blaise Pascal

Take some time to actually write your own (and hopefully more concise)
message to demonstrate you understand what you're changing. AI messages
are overly verbose.

> +	/*
> +	 * A command that has received all of its data and is only waiting
> +	 * for the data digest sits in RECV_DDGST: need_data_in() is already
> +	 * false, so nvmet_tcp_uninit_data_in_cmds() below skips it, yet it
> +	 * still holds the reference from nvmet_req_init(). Drop it here,
> +	 * while rcv_state still reflects it, so the SQ percpu_ref can drain
> +	 * and nvmet_sq_destroy() can complete. INIT_FAILED took no ref.
> +	 */

Same here.

> +	if (queue->rcv_state == NVMET_TCP_RECV_DDGST && queue->cmd &&
> +	    !(queue->cmd->flags & NVMET_TCP_F_INIT_FAILED))
> +		nvmet_req_uninit(&queue->cmd->req);

I don't think this criteria is even correct. RECV_DDGST doesn't mean the
command has all its data since that state is per-PDU.
nvmet_tcp_try_recv_data() enters that state at every H2C boundary, not
just the last one.



More information about the Linux-nvme mailing list