Commit db5a5406 authored by Daniel Wagner's avatar Daniel Wagner Committed by Keith Busch
Browse files

nvmet-fc: move lsop put work to nvmet_fc_ls_req_op



It’s possible for more than one async command to be in flight from
__nvmet_fc_send_ls_req. For each command, a tgtport reference is taken.

In the current code, only one put work item is queued at a time, which
results in a leaked reference.

To fix this, move the work item to the nvmet_fc_ls_req_op struct, which
already tracks all resources related to the command.

Fixes: 710c69db ("nvmet-fc: avoid deadlock on delete association path")
Reviewed-by: default avatarHannes Reinecke <hare@suse.de>
Signed-off-by: default avatarDaniel Wagner <wagi@kernel.org>
Signed-off-by: default avatarKeith Busch <kbusch@kernel.org>
parent 6ff1bd78
Loading
Loading
Loading
Loading
+9 −10
Original line number Diff line number Diff line
@@ -54,6 +54,8 @@ struct nvmet_fc_ls_req_op { /* for an LS RQST XMT */
	int				ls_error;
	struct list_head		lsreq_list; /* tgtport->ls_req_list */
	bool				req_queued;

	struct work_struct		put_work;
};


@@ -111,8 +113,6 @@ struct nvmet_fc_tgtport {
	struct nvmet_fc_port_entry	*pe;
	struct kref			ref;
	u32				max_sg_cnt;

	struct work_struct		put_work;
};

struct nvmet_fc_port_entry {
@@ -235,12 +235,13 @@ static int nvmet_fc_tgt_a_get(struct nvmet_fc_tgt_assoc *assoc);
static void nvmet_fc_tgt_q_put(struct nvmet_fc_tgt_queue *queue);
static int nvmet_fc_tgt_q_get(struct nvmet_fc_tgt_queue *queue);
static void nvmet_fc_tgtport_put(struct nvmet_fc_tgtport *tgtport);
static void nvmet_fc_put_tgtport_work(struct work_struct *work)
static void nvmet_fc_put_lsop_work(struct work_struct *work)
{
	struct nvmet_fc_tgtport *tgtport =
		container_of(work, struct nvmet_fc_tgtport, put_work);
	struct nvmet_fc_ls_req_op *lsop =
		container_of(work, struct nvmet_fc_ls_req_op, put_work);

	nvmet_fc_tgtport_put(tgtport);
	nvmet_fc_tgtport_put(lsop->tgtport);
	kfree(lsop);
}
static int nvmet_fc_tgtport_get(struct nvmet_fc_tgtport *tgtport);
static void nvmet_fc_handle_fcp_rqst(struct nvmet_fc_tgtport *tgtport,
@@ -367,7 +368,7 @@ __nvmet_fc_finish_ls_req(struct nvmet_fc_ls_req_op *lsop)
				  DMA_BIDIRECTIONAL);

out_putwork:
	queue_work(nvmet_wq, &tgtport->put_work);
	queue_work(nvmet_wq, &lsop->put_work);
}

static int
@@ -388,6 +389,7 @@ __nvmet_fc_send_ls_req(struct nvmet_fc_tgtport *tgtport,
	lsreq->done = done;
	lsop->req_queued = false;
	INIT_LIST_HEAD(&lsop->lsreq_list);
	INIT_WORK(&lsop->put_work, nvmet_fc_put_lsop_work);

	lsreq->rqstdma = fc_dma_map_single(tgtport->dev, lsreq->rqstaddr,
				  lsreq->rqstlen + lsreq->rsplen,
@@ -447,8 +449,6 @@ nvmet_fc_disconnect_assoc_done(struct nvmefc_ls_req *lsreq, int status)
	__nvmet_fc_finish_ls_req(lsop);

	/* fc-nvme target doesn't care about success or failure of cmd */

	kfree(lsop);
}

/*
@@ -1410,7 +1410,6 @@ nvmet_fc_register_targetport(struct nvmet_fc_port_info *pinfo,
	kref_init(&newrec->ref);
	ida_init(&newrec->assoc_cnt);
	newrec->max_sg_cnt = template->max_sgl_segments;
	INIT_WORK(&newrec->put_work, nvmet_fc_put_tgtport_work);

	ret = nvmet_fc_alloc_ls_iodlist(newrec);
	if (ret) {