Commit 27d79ee3 authored by Shuhei Matsumoto's avatar Shuhei Matsumoto Committed by Tomasz Zawadzki
Browse files

lib/iscsi: Wait until all PDUs are flushed for LUN hotplug



We ensure current pending tasks are aborted and no new task or PDU
is pending after LUN is unplugged now.  Then we have to ensure all
task completions and aborts are sent to initiator.

However, we had removed pending tasks without any notification to
initiator.  For this implementation, we are not surprised if initiator
waits for in-flight commands for a long time.

Due to recent refinements of LUN hotplug, we can wait safely that
all existing tasks complete or abort.

In _iscsi_conn_hotremove_lun(), after aborting R2Ts, start a poller
to wait until all PDUs for the LUN are flushed.  Then close the LUN.
We can safely free deferred PDUs for the LUN because they are
already flushed.

Signed-off-by: default avatarShuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Change-Id: I869b79a7c93d2e8a4a1577cc20d3b466548dfaaa
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/476033


Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarChangpeng Liu <changpeng.liu@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
parent 081153a8
Loading
Loading
Loading
Loading
+53 −3
Original line number Diff line number Diff line
@@ -447,6 +447,7 @@ iscsi_conn_close_lun(struct spdk_iscsi_conn *conn, int lun_id)

	spdk_scsi_lun_free_io_channel(iscsi_lun->desc);
	spdk_scsi_lun_close(iscsi_lun->desc);
	spdk_poller_unregister(&iscsi_lun->remove_poller);
	free(iscsi_lun);

	conn->luns[lun_id] = NULL;
@@ -462,13 +463,62 @@ iscsi_conn_close_luns(struct spdk_iscsi_conn *conn)
	}
}

static bool
iscsi_conn_check_tasks_for_lun(struct spdk_iscsi_conn *conn,
			       struct spdk_scsi_lun *lun)
{
	struct spdk_iscsi_pdu *pdu, *tmp_pdu;
	struct spdk_iscsi_task *task;

	assert(lun != NULL);

	/* We can remove deferred PDUs safely because they are already flushed. */
	TAILQ_FOREACH_SAFE(pdu, &conn->snack_pdu_list, tailq, tmp_pdu) {
		if (lun == pdu->task->scsi.lun) {
			TAILQ_REMOVE(&conn->snack_pdu_list, pdu, tailq);
			spdk_iscsi_conn_free_pdu(conn, pdu);
		}
	}

	TAILQ_FOREACH(task, &conn->queued_datain_tasks, link) {
		if (lun == task->scsi.lun) {
			return false;
		}
	}

	/* This check loop works even when connection exits in the middle of LUN hotplug
	 *  because all PDUs in write_pdu_list are removed in iscsi_conn_free_tasks().
	 */
	TAILQ_FOREACH(pdu, &conn->write_pdu_list, tailq) {
		if (pdu->task && lun == pdu->task->scsi.lun) {
			return false;
		}
	}

	return true;
}

static int
iscsi_conn_remove_lun(void *ctx)
{
	struct spdk_iscsi_lun *iscsi_lun = ctx;
	struct spdk_iscsi_conn *conn = iscsi_lun->conn;
	struct spdk_scsi_lun *lun = iscsi_lun->lun;
	int lun_id = spdk_scsi_lun_get_id(lun);

	if (!iscsi_conn_check_tasks_for_lun(conn, lun)) {
		return -1;
	}
	iscsi_conn_close_lun(conn, lun_id);
	return -1;
}

static void
_iscsi_conn_hotremove_lun(void *ctx)
{
	struct spdk_iscsi_lun *iscsi_lun = ctx;
	struct spdk_iscsi_conn *conn = iscsi_lun->conn;
	struct spdk_scsi_lun *lun = iscsi_lun->lun;
	int lun_id = spdk_scsi_lun_get_id(lun);

	assert(spdk_io_channel_get_thread(spdk_io_channel_from_ctx(conn->pg)) ==
	       spdk_get_thread());
@@ -479,9 +529,9 @@ _iscsi_conn_hotremove_lun(void *ctx)
	}

	spdk_clear_all_transfer_task(conn, lun, NULL);
	_iscsi_conn_free_tasks(conn, lun);

	iscsi_conn_close_lun(conn, lun_id);
	iscsi_lun->remove_poller = spdk_poller_register(iscsi_conn_remove_lun, iscsi_lun,
				   1000);
}

static void
+1 −0
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@ struct spdk_iscsi_lun {
	struct spdk_iscsi_conn		*conn;
	struct spdk_scsi_lun		*lun;
	struct spdk_scsi_lun_desc	*desc;
	struct spdk_poller		*remove_poller;
};

struct spdk_iscsi_conn {